Правки
All checks were successful
Deploy / build-and-publish (push) Successful in 1m41s
Deploy / deploy (push) Successful in 13s

This commit is contained in:
2024-10-08 22:13:07 +03:00
parent f3cbf44944
commit 48c0b05d99

View File

@@ -4,16 +4,16 @@ date: "2024-10-08"
--- ---
<script> <script>
import ArticleTitle from "$lib/components/ArticleTitle.svelte" import ArticleTitle from "$lib/components/ArticleTitle.svelte";
import MaterialSymbolsErrorCircleRoundedOutline from '~icons/material-symbols/error-circle-rounded-outline'; import MaterialSymbolsErrorCircleRoundedOutline from "~icons/material-symbols/error-circle-rounded-outline";
import Tooltip from "$lib/components/Tooltip.svelte" import Tooltip from "$lib/components/Tooltip.svelte";
</script> </script>
<ArticleTitle metadata={metadata} /> <ArticleTitle metadata={metadata} />
На днях мне вечером позвонил давний друг. На днях мне позвонил давний друг.
Я на тот момент уже успел уснуть, поэтому его звонок застал меня врасплох, особенно учитывая, На тот момент я спал, и его звонок застал меня врасплох, особенно учитывая,
что он начал пытаться мне объяснить как заработать на создании шортсов на Youtube. что он начал пытаться объяснить мне, как заработать на создании шортсов на Youtube.
Он увидел [такое видео](https://www.youtube.com/shorts/iMBJH3Eo8M0) и вбил его в [Socialblade](https://socialblade.com/youtube/channel/UCBcGha5FjnmsaOhMJJeXtIQ). Он увидел [такое видео](https://www.youtube.com/shorts/iMBJH3Eo8M0) и вбил его в [Socialblade](https://socialblade.com/youtube/channel/UCBcGha5FjnmsaOhMJJeXtIQ).
У автора приблизительный доход -- **1.5 \- 24 (к$/мес)**. У автора приблизительный доход -- **1.5 \- 24 (к$/мес)**.
@@ -27,8 +27,8 @@ date: "2024-10-08"
Я давно разочаровался в методах заработка, которые вращаются вокруг того, что надо стать знаменитым в интернете. Я давно разочаровался в методах заработка, которые вращаются вокруг того, что надо стать знаменитым в интернете.
Но я сказал, что посмотрю и поразбираюсь в том, как можно было бы сделать такое видео, Но я сказал, что посмотрю и поразбираюсь в том, как можно было бы сделать такое видео,
но Youtube каналом заниматься не буду. но Youtube каналом заниматься не буду.
Если честно, то я не очень, то и рассчитывал всерьез этим заниматься, но я уже проснулся, Если честно, то я не очень-то и рассчитывал всерьез этим заниматься, но я уже проснулся,
спать было неохота и я уселся думать над тем, как можно повторить такую симуляцию. спать было неохота, и я уселся думать над тем, как можно повторить такую симуляцию.
Друг ко мне обратился, потому что знал, что я когда-то <Tooltip><MaterialSymbolsErrorCircleRoundedOutline Друг ко мне обратился, потому что знал, что я когда-то <Tooltip><MaterialSymbolsErrorCircleRoundedOutline
class='inline' class='inline'
@@ -42,8 +42,8 @@ date: "2024-10-08"
Фреймворк с очень низким порогом вхождения, позволяющий создавать 2D игры на Lua. Фреймворк с очень низким порогом вхождения, позволяющий создавать 2D игры на Lua.
Единственная его проблема -- сообщество очень любит "велосипеды". Единственная его проблема -- сообщество очень любит "велосипеды".
Библиотек по пальцам пересчитать и пакета способного воспроизводить Midi нет и в помине. Библиотек по пальцам пересчитать, и пакета способного воспроизводить Midi нет и в помине.
Но я решил решать проблемы по порядку и взялся за то, что точно могу сделать -- графику. Но я решил разбираться с проблемами по порядку и взялся за то, что точно могу сделать -- графику.
## Графика ## Графика
@@ -55,9 +55,9 @@ local windowH = 720
local centerX = windowW / 2 local centerX = windowW / 2
local centerY = windowH / 2 local centerY = windowH / 2
local circleCount = 6 local circleCount = 6 -- Количество окружностей
local radiusIncrement = 50 local radiusIncrement = 50 -- Расстояние между окружностями
local circleResolution = 36 local circleResolution = 36 -- Сколько градусов приходится на один сегмент
local gravity = 180 local gravity = 180
local circles = {} local circles = {}
@@ -65,9 +65,9 @@ local ball = {
x = centerX, x = centerX,
y = centerY, y = centerY,
radius = 7, radius = 7,
speedX = 100 * love.math.random() - 50, speedX = 100 * love.math.random() - 50, -- Дадим случайную начальную скорость мячику
speedY = 100 * love.math.random() - 50, speedY = 100 * love.math.random() - 50,
color = {1, 1, 1} color = {1, 1, 1} -- Белый
} }
``` ```
@@ -80,13 +80,14 @@ local ball = {
### `love.load()` ### `love.load()`
В функции `love.load()` создадим нужные нам окружности. В функции `love.load()` создадим нужные нам окружности.
Они состоят из арок и, чтобы арки на внешних окружностях не были слишком большими, Они состоят из арок, и, чтобы арки на внешних окружностях не были слишком большими,
умножаем разрешение окружностей на их номер по порядку от центра. умножаем разрешение окружностей на их номер по порядку от центра.
```lua ```lua
function love.load() function love.load()
love.window.setMode(720, 720, {highdpi = true}) love.window.setMode(720, 720, {highdpi = true})
-- Создаем несколько окружностей
for i = 1, circleCount do for i = 1, circleCount do
local radius = radiusIncrement * i local radius = radiusIncrement * i
local angleStart = 0 local angleStart = 0
@@ -94,6 +95,7 @@ function love.load()
local segments = {} local segments = {}
local boostedResolution = circleResolution / i local boostedResolution = circleResolution / i
-- Заполняем окружности сегментами, в соответствии с разрешением
for angle = angleStart, angleEnd - boostedResolution, boostedResolution do for angle = angleStart, angleEnd - boostedResolution, boostedResolution do
local angleRadStart = math.rad(angle) local angleRadStart = math.rad(angle)
local angleRadEnd = math.rad(angle + boostedResolution) local angleRadEnd = math.rad(angle + boostedResolution)
@@ -115,6 +117,7 @@ function love.load()
end end
circles[i] = { circles[i] = {
-- У каждой окружности будет случайный цвет
color = { color = {
love.math.random(), love.math.random(),
love.math.random(), love.math.random(),
@@ -157,7 +160,7 @@ circles = {
Для этого сначала опишем функцию поиска пересечения окружности и линии. Для этого сначала опишем функцию поиска пересечения окружности и линии.
В роли окружности будет наш мячик, а в роли линии сегмент круга. В роли окружности будет наш мячик, а в роли линии сегмент круга.
Конечно сегмент на самом деле не прямая линия, а арка, но никто не заметит, что при столкновении он ведет себя как линия. Конечно, сегмент, на самом деле, не прямая линия, а арка, но никто не заметит, что при столкновении он ведет себя как линия.
```lua ```lua
function isCollidingWithLine(ball, arc) function isCollidingWithLine(ball, arc)
@@ -196,11 +199,11 @@ function isCollidingWithLine(ball, arc)
end end
``` ```
Может произойти, что наш шарик одновременно пересечет 2 сегмента. Может произойти так, что наш шарик одновременно пересечет 2 сегмента.
Если никак от этого не защититься, то получится, что он 2 раза за кадр поменяет свое направление на 180º Если никак от этого не защититься, то получится, что он 2 раза за кадр поменяет свое направление на 180º
и как будто пролетит сквозь препятствие. и как будто пролетит сквозь препятствие.
Поэтому храним информацию о том сталкивался ли уже мячик с чем-то и один раз в кадр даем ему это сделать. Поэтому храним информацию о том, сталкивался ли уже мячик с чем-то и один раз в кадр даем ему это сделать.
```lua ```lua
function love.update(dt) function love.update(dt)
@@ -248,7 +251,7 @@ end
### `love.draw()` ### `love.draw()`
Ну и наконец отрисовка Ну и, наконец, отрисовка
```lua ```lua
function love.draw() function love.draw()
@@ -260,8 +263,8 @@ function love.draw()
love.graphics.arc( love.graphics.arc(
"line", "line",
"open", "open",
windowW/2, centerX,
windowH/2, centerY,
radiusIncrement * i, radiusIncrement * i,
math.rad(arc.angleStart), math.rad(arc.angleStart),
math.rad(arc.angleEnd) math.rad(arc.angleEnd)
@@ -288,14 +291,14 @@ end
## Звук ## Звук
Раз возможности воспроизводить Midi файлы из самой программы у нас нет, напишем отдельный скрипт на Python, Раз возможности воспроизводить Midi файлы из самой программы у нас нет, напишем отдельный скрипт на Python,
который будет за это отвечать. Благо в экосистеме Питона нет недостатка готовых библиотек. который будет за это отвечать. Благо, в экосистеме Питона нет недостатка готовых библиотек.
Принцип в том, что графическая часть будет писать в файл время каждого удара, а скрипт на питоне можно Принцип в том, что графическая часть будет писать в файл время каждого удара, а скрипт на питоне можно
будет потом запустить и получить звук, который будет синхронизирован с видеорядом, если мы таковой будет потом запустить и получить звук, который будет синхронизирован с видеорядом, если мы таковой
записали с экрана. записали с экрана.
Midi файл представляет собой очень плотно упакованный бинарный файл, который просто так, как JSON не откроешь, Midi файл представляет собой очень плотно упакованный бинарный файл, который просто так, как JSON не откроешь,
но если его расшифровать то он состоит из событий, они делятся на несколько типов: но если его расшифровать, то он состоит из событий. Они делятся на несколько типов:
- Note Off, - Note Off,
- Note On, - Note On,
@@ -339,7 +342,7 @@ def play_midi_file(midi_file, timestamps):
play_midi_file(midi_file, timestamps) play_midi_file(midi_file, timestamps)
``` ```
Этот скрипт можно воспринимать как будто мы подключили к компьютеру клавиатуру и скрипт играет на Этот скрипт можно воспринимать, как будто мы подключили к компьютеру клавиатуру, и скрипт играет на
ней выбранную нами песню в нужном нам темпе. ней выбранную нами песню в нужном нам темпе.
Проблема в том, что если вы подключите физическую клавиатуру к компьютеру и начнете нажимать по клавишам, Проблема в том, что если вы подключите физическую клавиатуру к компьютеру и начнете нажимать по клавишам,
то вы ничего не услышите. Компьютеру неоткуда знать, что делать с этим вводом. то вы ничего не услышите. Компьютеру неоткуда знать, что делать с этим вводом.