Перейти к содержимому
Oleshe

OpenGames 2

Рекомендуемые сообщения

Привет! Последние 2 месяца я разрабатывал OE 2. Это проект очень схожий с Unity имея дочерний замысел с OpenGames где после изменения объекта не надо писать draw()...

Он еще не готов, я хотел релизнуть после того как я его доделаю, но случилось не предвиденное. Из AppMarket-a была удалена либа opengames, что напрямую убивает весь первый движок т.к. оно в зависимостях, и в зависимостях всех дочерних приложений. Мне удалять это было не зачем, да он был поломанный, там объект кнопки не работал. Но это как-то тупо сносить один элемент из 2-, не так-ли?

 

"Какая вообще разница?" Этот проект остался не доделанным, движок готов, установщик движка тоже. Не готов только редактор. Мне симпатизирует этот проект, но я прекращу (или сильно дестабилизирую) свою собственную поддержку данного проекта т.к. не исключаю случайное исчезновение моих продуктов в макрете.

 

Так-же одна из причин это полу-мёртвое комьюнити. За пол года существования первого OpenGames им увлёкся только один человек. И то всё его творчество (хоть и довольно спорное (вирусы(удаление одной папки\файла))) хоть и было довольно спорное, но было удалено самим им. А зачем удалять свой продукт?

Движок не нашел людей, а зачем разрабатывать предложение, если нету спроса.


Это репозиторий проекта, в нём есть подробная (имхо) вики которая поясняет как работать в движке из скрипта. Так-же есть 2 примера которые могут перекидываться сообщениями между друг-другом. Там целая куча файлов которая добавляет функций в движок с определённой задачей.

 

Цель этого поста: сказать что уже есть неплохие(имхо) наработки графического движка, если кто-то уже собрался что-то делать.

 

Скриншот первой программы для того что-бы разбавить текст:

Скрытый текст

image.thumb.png.cabaf52854f2e020c399f0fb56c4c0bc.png

 

Изменено пользователем Oleshe

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1 час назад, Oleshe сказал:

Цель этого поста: сказать что уже есть неплохие(имхо) наработки графического движка, если кто-то уже собрался что-то делать.

Тут есть смыл рассказать о возможностях этого движка, зачем он нужен, кому он может пригодиться, и как им пользоваться.

 

1 час назад, Oleshe сказал:

За пол года существования первого OpenGames им увлёкся только один человек.

Кстати, людей как раз и может привлечь хорошее описание. Какой бы хорошей ни была программа, пока ты о ней доходчиво не расскажешь, её даже скачивать, скорее всего, никто не будет. А убедить других программистов пользоваться своей библиотекой или движком будет ещё сложнее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
18 часов назад, eu_tomat сказал:

Тут есть смыл рассказать о возможностях этого движка, зачем он нужен, кому он может пригодиться, и как им пользоваться.

А, да, извините.
Итак, для того что-бы им вообще можно было воспользоваться нужно загрузить Main.lua как библиотеку. Это делается примерно так:

local OE = loadfile("/OpenGames 2/Main.lua")()

Поскольку он "модульный" можно менять расположение главной папки. Оно будет записано в System.getUserSettings.OpenGames2EnginePath (Если вы устанавливали с установщика).

Сделаем для примера радужный квадрат который по клику переместиться к курсору.

Создадим наш квадрат, это панель. Существует множество разных объектов которые подвластны нам, такие как ComboBox, Button, Input, Switch и т.д.

local obj = OE.createObject() -- Создаём объект которые сам добавляется в сцену.
obj:setRenderMode(OE.Render.renderTypes.PANEL) -- Рендерить как панель.
local matID = obj:addComponent(OE.Component.componentTypes.MATERIAL) -- Что-бы у нашего квадрата был цвет
obj.Transform.Scale.Width = 10 -- Размер длинны
obj.Transform.Scale.Height = 5 -- Размер высоты, в два раза меньше длинны

После сделаем окно и покажем пользователю:

OE.Project.Window.Color = 0x505050
OE.Project.Window.Width = 160
OE.Project.Window.Height = 50
OE.Project.Window.Title = "Это куб"
OE.initWindow() -- Принять нынешние значения и пересоздать окно с ними

Создадим файл для нашего скрипта и добавим его к нашему объекту:

local function update(...)
	local args = {...}
  	args[1].Components[matID].Color.First = math.random(0x0,0xFFFFFF) -- Именение цвета. Поскольку update вызываеться asap оно бует менять его довольно выстро. Что нам вроде-бы и надо.
	if args[2].lastEvent[1] == "drag" or args[2].lastEvent[1] == "drop" or args[2].lastEvent[1] == "touch" then -- lastEvent это буквально последнее событие
   		args[1].Transform.Position.x = args[2].lastEvent[3]-math.ceil(args[1].Transform.Scale.Width/2)-args[2].Render.Window.x -- args[1] Это объект от имени которого запустился скрипт. args[2] это OE для вызываемых скриптов. То-есть мы находим середину для x относительно того куда мы кликнули, ..
    	args[1].Transform.Position.y = args[2].lastEvent[4]-math.ceil(args[1].Transform.Scale.Height/2)-args[2].Render.Window.y -- .. как и для y
	end
end
OE.Storage.createFile(OE.CurrentScene.Storage,'Script.lua',{Update=update})
obj.Components[obj:addComponent(OE.Component.componentTypes.SCRIPT)].file = "Script.lua" -- addComponent вернёт номер компонента в obj.Components, а мы его сразу подхватываем и изменяем значение file для скриптa на наш файл. Оно найдёт его везде, в файлах сцены или файлах проекта.
OE.Script.Reload() -- Перезагружаем все наши скрипты добовляя наш.

Добавим нашу милашку к рендеру и готово

obj:addToRender()

Итог:

local OE = loadfile("/OpenGames 2/Main.lua")()
local obj = OE.createObject()
obj:setRenderMode(OE.Render.renderTypes.PANEL)
local matID = obj:addComponent(OE.Component.componentTypes.MATERIAL)
obj.Transform.Scale.Width = 10 
obj.Transform.Scale.Height = 5 
OE.Project.Window.Color = 0x505050
OE.Project.Window.Width = 160
OE.Project.Window.Height = 50
OE.Project.Window.Title = "Это куб"
OE.initWindow()
local function update(...)
	local args = {...}
  	args[1].Components[matID].Color.First = math.random(0x0,0xFFFFFF)
	if args[2].lastEvent[1] == "drag" or args[2].lastEvent[1] == "drop" or args[2].lastEvent[1] == "touch" then
   		args[1].Transform.Position.x = args[2].lastEvent[3]-math.ceil(args[1].Transform.Scale.Width/2)-args[2].Render.Window.x
    	args[1].Transform.Position.y = args[2].lastEvent[4]-math.ceil(args[1].Transform.Scale.Height/2)-args[2].Render.Window.y
	end
end
OE.Storage.createFile(OE.CurrentScene.Storage,'Script.lua',{Update=update})
obj.Components[obj:addComponent(OE.Component.componentTypes.SCRIPT)].file = "Script.lua"
OE.Script.Reload()
obj:addToRender()

Теперь давайте сделаем тоже самое в сырую...

Итог:

local GUI = require("GUI")
local System = require('System')
local wk,win = System.addWindow(GUI.titledWindow(1,1,160,50,'Это куб',true))
win.backgroundPanel.colors.background = 0x505050
win.titlePanel.colors.background = 0x505050
local panel = win:addChild(GUI.panel(1,1,10,5,0x0))
wk.eventHandler = function(_,_,...)
  local args = {...}
  panel.colors.background = math.random(0,0xFFFFFF)
  if args[1] == 'touch' or args[1] == 'drop' or args[1] == 'drag' then
    panel.localX = args[3]-math.ceil(panel.width/2)-win.localX
    panel.localY = args[4]-math.ceil(panel.height/2)-win.localY
  end
end
win:draw()

По символам он меньше. 

В обоих OE есть редактор в котором можно наглядно отредактировать расположение объектов, а в сыром запускай кучу раз.

Минусы и плюсы использование движка:

+ Просто для освоения

+ Наглядно

- Грамоздко (большие файлы)

- События медленные (Разница между сырым и движком видна, причём очень сильно. Не знаю с чем это связано.)

Можно еще сравнить Unity и какой ни-будь pygame. Разница размера файлов колоссальная, но большинство используют юнити т.к. в pygame и я не очень разобрался...

Я не очень понимаю как находить сильные и слабые стороны в своём-же продукте...

Изменено пользователем Oleshe

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Шли года... Буксующая разработка продолжалась.. И вот 0.4! Считаю её стабильной и лучшей на данный момент, потому отметим её.
Забудьте всё, теперь всё по-другому. Разберём попытку перенести одну игру, но провалившеюся по одной причине, которую мы разберём ниже.
Здесь находится Main.lua. Разберём что же творится!

Скрытый текст

local scriptPath = string.gsub(require('System').getCurrentScript(),'/LiminalStarwave.app','')
local args = {...}
_, args = require('System').parseArguments(table.unpack(args))
local OE, why = assert(loadfile(scriptPath))() -- Подгрузка библиотеки
if not OE then print(why) return end
OE.applicationRoot = string.gsub(require('System').getCurrentScript(),'Main.lua','')
OE.fileRoot = OE.applicationRoot .. 'Additional_Content/' -- Ставим рут приложения и папки контента
OE.createScene('Game') -- Создаём основную сцену
OE.Render.addLayer() -- Main game
OE.Render.layers[2].y = -16
OE.Render.addLayer() -- Arrows and fly windows
OE.Render.setResolution(135,50) -- Добавляем еще 2 слоя к первому, итого 3. 1 - задник, 2 - игра, 3 - UI Ставим разрешение 135 на 50. 
local sky = OE.createObject('Skybox','Game') -- Задник
sky:_addScript('MAIN_Panel.lua',"Render") -- Добавляем скрипт панели
sky.Render.color = 0
sky.Transform.Scale = {w = 135, h = 50} -- Параметры панели. Её размер и цвет.

local BL = OE.createObject('BottomLine','Game') -- Две чёрные полосы сверху и снизу экрана по такому же принципу, как и задник
BL:_addScript('MAIN_Panel.lua',"Render")
BL.Render.color = 0
BL.Render.layer = 3 -- .. с отличием, что мы ставим ему 3 слой
BL.Transform.Scale = {w = 135, h = 3}
BL.Transform.Position = {y=48}
local UL = OE.createObject('UpperLine','Game')
UL:_addScript('MAIN_Panel.lua',"Render")
UL.Render.color = 0
UL.Render.layer = 3
UL.Transform.Scale = {w = 135, h = 3}

local BG = OE.createObject('Background','Game') -- Пикча задника игры.
BG:_addScript('MAIN_Sprite.lua','Render')
BG.Render.sourceImage = 'Background.pic'
BG.Render.transperent = true -- Она прозрачна.
BG.Render.layer = 2

local ENA = OE.createObject('ENA','Game') -- Наш персонаж
ENA:_addScript('MAIN_Sprite.lua','Render')
ENA.Render.sourceImage = 'ENAIdle1.pic'
ENA.Render.transperent = true
ENA.Render.layer = 2
ENA:_addScript('CharactersSpriteBehavior.lua','SpriteBehavior') -- Смотреть в следующем файле. Управляет анимацией спрайта.
ENA:_addScript('PlayerInput.lua','PlayerInput') -- Смотреть в очередном следующем файле. Управляет вводом игрока.
--ENA:_addScript('SoundManager.lua','Audio') -- Смотреть в третьем файле. Управляет звуком.
ENA.Transform.Position = {y=32}

OE.Storage.reloadAdditionalFiles(OE.fileRoot) -- Загружаем дополнительные ассеты из нашей папки

OE.log('Project init completed')
OE.loadScene("Game") -- Загружаем сцену
OE.log('Scene loaded')
OE.initWindow() -- И иницилизируем окно

./Additional_Content/Scripts/CharactersSpriteBehavior.lua


local currentAnim = "" -- Анимация, что сейчас воспроизводится
function Idle()
	currentAnim = "ENAIdle"
	Object.SpriteBehaviorAnimation.Play(currentAnim)
end

function Left()
	currentAnim = "ENALeft"
	Object.SpriteBehaviorAnimation.Play(currentAnim)
end
function Right()
	currentAnim = "ENARight"
	Object.SpriteBehaviorAnimation.Play(currentAnim)
end
function Down()
	currentAnim = "ENADown"
	Object.SpriteBehaviorAnimation.Play(currentAnim)
end
function Up()
	currentAnim = "ENAUp"
	Object.SpriteBehaviorAnimation.Play(currentAnim)
end

function ChangeSprite(index)
	Object.Render.setImage(currentAnim .. index .. ".pic") -- Сделал универсально. Анимация ENAIdle, индекс 1, .pic > ENAIdle1.pic
end

function OnEndNotIdle()
	OE.Script.Invoke(Idle, 0.2) -- В отличий от идле анимаций, другие чуть задержаться на последнем спрайте.
end

Start = Idle -- Когда сцена загрузится, мы переходим в Idle

function Init()
	Object:_addScript('MAIN_Animation.lua','SpriteBehaviorAnimation', true) -- При иницилизвации скрипта, добавляем компонент анимации. Просто выполняет функций по таймкоду
	Object.SpriteBehaviorAnimation.animations["ENAIdle"] = {
		onEnd = Idle, -- Выполнится, когда кейфреймов не останится
		frames = {
			[0] = ChangeSprite,
			[0.2] = ChangeSprite,
			[0.4] = ChangeSprite
		}
	}
	Object.SpriteBehaviorAnimation.animations["ENAUp"] = {
		onEnd = OnEndNotIdle,
		frames = {
			[0] = ChangeSprite,
			[0.1] = ChangeSprite -- У нас только 2 спрайта на каждое действие, в отличий от Idle
		}
	}
	Object.SpriteBehaviorAnimation.animations["ENADown"] = Object.SpriteBehaviorAnimation.animations["ENAUp"] -- Ничем не отличаются, ибо мы уже сделали всё достатачно универсальным
	Object.SpriteBehaviorAnimation.animations["ENALeft"] = Object.SpriteBehaviorAnimation.animations["ENAUp"]
	Object.SpriteBehaviorAnimation.animations["ENARight"] = Object.SpriteBehaviorAnimation.animations["ENAUp"]
end

./Additional_Content/Scripts/PlayerInput.lua


local KeyCodes, pauseToggle = OE.Input.keyCodes
local anims = {[KeyCodes.A] = "Left", [KeyCodes.S] = "Down", [KeyCodes.D] = "Right", [KeyCodes.W] = "Up"} -- Привязываем клавиши к соответствующим названиям функций, что стартуют анимации
function KeyDown(key) -- Когда клавиша нажата
	if anims[key] then -- Если клавиша действия, значит
		Object.SpriteBehavior[anims[key]]() -- запускаем действие
	elseif key == KeyCodes.SPACE then -- , или же, если это пробел
		if pauseToggle then -- — ставим на паузу, остарнавливая аудио, или же снимаем с паузы
			Time.scale = 0
			Object.Audio.Pause()
		else
			Time.scale = 1
			Object.Audio.Resume()
		end
		pauseToggle = not pauseToggle -- Переключаем паузу
	end
end

./Additional_Content/Scripts/SoundManager.lua


local cmp, getFile,len1,len2 = require('component'), OE.Storage.getFile
local empty = string.rep("\xAA",8192) -- Заранее подготавливаем пустую для кассет строку
local listOfTapes = cmp.list('tape_drive') -- Получаем и
tape1 = cmp.proxy(listOfTapes()) -- ставим прокси для обоих кассетниц
tape2 = cmp.proxy(listOfTapes())
local lengts = {} -- Хз зачем
if not tape1 or not tape2 then -- Если кассетниц менее 2х
	OE.log("Not enough tapes")
	return false
end
function Start()
	tape1.play()
	tape2.play()
end

function Update() -- Каждый кадр мы проверяем
	if tape1.getPosition() >= len2 then -- не закончилось ли аудио
		tape1.stop()
		tape2.stop()
		OE.exit() -- И если да — останавливаем звук и выходим
		-- BSOD picture, sound, crash system
	end
end

function Pause()
	tape1.stop()
	tape2.stop()
end
Resume = Start
function Wipe()
	tape1.seek(-math.huge)
	tape2.seek(-math.huge)
	local k = tape1.getSize()
	for i = 1, k + 8191, 8192 do
		tape1.write(empty)
		tape2.write(empty)
	end
	tape1.seek(-math.huge)
	tape2.seek(-math.huge)
end
function Dissolve()
	Pause()
	Wipe()
end
onApplicationExit = Dissolve -- При стандартном выходе также останавливаем и очищаем кассету

Dissolve()
tape1.seek(-math.huge)
tape2.seek(-math.huge)
tape1.write(getFile('Inst.dfpwm')) -- Записываем инструментал
len1 = #getFile('Inst.dfpwm')
OE.Storage.unloadFile("Inst.dfpwm") -- Выгружаем его из памяти

tape2.write(getFile('Voices.dfpwm')) -- Делаем тоже самое с голосами типо
len2 = #getFile('Voices.dfpwm')
OE.Storage.unloadFile("Voices.dfpwm")

tape1.seek(-math.huge)
tape2.seek(-math.huge)

 

Получается, все старые проекты работать не будут. Список изменений.. Большой. Если кратко:

Изменён рендер в очередной раз, тотально. Теперь он быстрей и работает на нашем движке Matrix, который можно выдернуть из движка прям так и использовать в своих сторонних проектах.

Добавлена поддержка сети, который точно также можно вырвать. У него еще есть OpenOS версия!! Просто измените пару строк в получении евентов и добавлении слушателей.
Добавлены компоненты: спрайт, кнопка(Как поставить кнопку, можете узнать в Test.app/Main.lua)
Все модули перенесены в отдельную для этого папку.
Самое модное: использование скриптов. Вы могли заметить, что мы ставим глобальные переменные и забираем их из, казалось бы, ниоткуда.
Все скрипты теперь таблица. Глобальные переменные можно получить по ней. Таблицы привязываются к объекту. Следовательно, можно сделать так:

OE.CurrentScene.Objects[1].Render.setColor(math.random(0,0xFFFFFF))

Или из самого скрипта:

Object.Render.setColor(math.random(0xFFFFFF))

Весь рендер перенесён с screen библиотеки на сырые вызовы гпу. С матриксом это не такая проблема, если рендер стоит в режиме perObject. Следовательно, был исправлен воистину тупой баг, что на прозрачных пикслеях изображения не оставались символы, из-за чего прозрачностью обладал только задник пикселя, а символ оставался пробелом, кушая концепт прозрачности.

Проблемы:

Слои в Render.layers никак не связаны, как бы это странно не звучало. Придётся придумать альтернативу дублированию библиотеки и рисовать в иерархии, ибо если, например, изменится 1й слой, пиксели на месте 2го исчезнут, ибо они буквально на другом холсте и не считаются для колайда и он считает, что всё ок.
Главная проблема: лаги. Из-за отрисовки изображения выяснилось, что рисовать такой большой спрайт даже в буфере — дорого. Очень. Идея для фикса уже есть, но, всё же, исправит ситуацию он не сильно, ибо всё сейчас упирается в скорость цикла.

Такие пироги.

Вот видео, например:

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

В виду низкого call-бюджета реализовать полноэкранную анимацию с приемлемым fps не представляется. возможным. 

Но совмещая gpu.copy и редко используемые gpu.bitblt можно добиться определённого успеха в борьбе за частоту обновления игровой картинки. Если к этому добавить смекалку и учесть, что мод OpenComputers обычно используется внутри майнкрафта, то можно физически блокировать обзор частей экрана и использовать их как быстый буфер для хранения спрайтов выводимых на обозримую пользователем часть экрана. Например, в игре поддерживаются разрешения для экрана 3-го тира находящиеся в пределах 8000 символов. То есть можно установить разрешение экрана как 160 х 50, так и 100 х 80! (так же поддерживается в последних версиях Оцелота). И если спрятать половину этой площади (допустим, под уровнем пола помещения где располагается экран), то в обозримой части экрана всё ещё останется 40 строк (200 х 160 точек). Конечно, это уже не 256 х 192: полноценных портов Танчиков, Марио, Контры, Кастлевании и прочих шедевров не сваять. Но несмотря на это - появляется, пусть и изначально дефектный, но несмотря на это вполне работоспособный механизм для построения простых игр на спрайтах. И на основе того же самого Super Mario Bros можно сваять продукт с приемлемым глазу fps. Экран будет чуть меньше, чем в ниже приведённом примере, а процесс плавнее. Впрочем, у каждого продукта свои задачи. Возможно, кто-то делает аналог Юнити для пентиума-2, как знать....

 

А вот и ссылка на проект с использованием gpu.copy:

https://www.reddit.com/r/OpenComputers/comments/olo832/i_remade_super_mario_bros_on_opencomputers/

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да-да, поразмыслив, кажется, использовать буферы на отдельное изображение — оно. Только проблема скорее в динамичности и объёме изображения. Если изменится элемент выше нашего, или, если он прозрачный, под ним, придётся ВСЁ пересчитывать, что как раз-таки очень дорого, даже если отрисовывать только ту область, что изменилась (Что и происходит в примере)
Тем не менее, со статичной картинкой работать проще... Заранее просчитать и добавить в буфер все варианты спрайтов, а после доставать их на нужное место. Но с динамичной получится слишком много вариантов, из-за чего это увы полный. Что поделать... Пойду действительно попробую реализовать это... Маленький рефакторинг, в третий раз переписать движок рендера...

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Если цель - просто искусство, то можно изменить параметры стоимости вызовов в настройках.

 

А если юзер френдли без шаманства в конфигах - то да, раскадровыватьтдинамическую картинку, сверять какие поля 2х4 или 4х4 совпали и по ним делать статичную карту. Ведь какие-то её части всё равно будут неизменны. Конечно, составлять спрайт (допустим) 48х48 из символов дороже по времени. Но сохранять много целых кадров 48х48 дорого уже по занимаемой области на экране.

В любом случае движок исполняющий анимацию уже есть, и благодаря ему рутина создания целых фреймов или фрагментированных кусков не столь страшна.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...