Hello,
I tried to create a game-engine library using my self created class library.
I had to create my own class library, because the common way of using setmetatable is not working.
Class Library:
local class = {}
function class:new(value)
value = value or {}
local new_class = {}
for cindex, cdata in pairs(self) do
if type(cdata) == "table" then
new_class[cindex] = self:t_rebuild(cdata)
else
new_class[cindex] = cdata
end
end
for vindex, vdata in pairs(value) do
if type(vdata) == "table" then
new_class[vindex] = self:t_rebuild(vdata)
else
new_class[vindex] = vdata
end
end
return new_class
end
function class:destroy()
self = nil
end
function class:t_rebuild(my_table)
my_table = my_table or {}
local new_table = {}
for index, data in pairs(my_table) do
if type(data) == "table" then
new_table[index] = self:t_rebuild(data)
else
new_table[index] = data
end
end
return new_table
end
return { class = class }
Game-engine Library:
if fs.exists("class") == false then shell.run("wget https://raw.githubusercontent.com/Terandox-The-Pineapple/TRX-Librarys/main/class.lua class") end
local class_lib = require("class")
local players = {}
local enemys = {}
local others = {}
local backgrounds = {}
backgrounds.backgroundlist = {}
backgrounds.selected = 1
local menus = {}
menus.menulist = {}
menus.selected = 1
local controllers = {}
local render = {}
function render:init()
for x = 1, 51, 1 do
if self[x] == nil then self[x] = {} end
for y = 1, 19, 1 do
if self[x][y] == nil then self[x][y] = {} end
self[x][y].color = false
self[x][y].text = false
end
end
end
render = class_lib.class:new(render)
local entity = { posX = 1, posY = 1, render = false }
function entity:getSize()
local sizeX = 0
local sizeY = 0
for x = 1, 51, 1 do
local countY = 0
for y = 1, 19.1 do
local countX = 0
if self.render[x][y].color ~= false or self.render[x][y].text ~= false then countY = countY + 1 end
for inner_x = 1, 51, 1 do
if self.render[inner_x][y].color ~= false or self.render[inner_x][y].text ~= false then countX = countX + 1 end
end
if countX > sizeX then sizeX = countX end
end
if countY > sizeY then sizeY = countY end
end
return sizeX, sizeY
end
function entity:moveRight()
local sizeX, sizeY = self:getSize()
if self.posX <= (51 - sizeX) then self.posX = self.posX + 1 end
end
function entity:moveLeft()
if self.posX > 1 then self.posX = self.posX - 1 end
end
function entity:moveUp()
if self.posY > 1 then self.posY = self.posY - 1 end
end
function entity:moveDown()
local sizeX, sizeY = self:getSize()
if self.posY <= (19 - sizeY) then self.posY = self.posY + 1 end
end
function entity:getLastPosition()
local sizeX, sizeY = self:getSize()
return self.posX + (sizeX - 1), self.posY + (sizeY - 1)
end
function entity:draw()
local sizeX, sizeY = self:getSize()
for x = 1, sizeX, 1 do
for y = 1, sizeY, 1 do
if self.render[x][y].color ~= false or self.render[x][y].text ~= false then
term.setCursorPos(x + (self.posX - 1), y + (self.posY - 1))
if self.render[x][y].color ~= false then
term.setBackgroundColor(self.render[x][y].color)
else
term.setBackgroundColor(backgrounds.backgroundlist[backgrounds.selected].render[x + (self.posX - 1)][y + (self.posY - 1)].color)
end
if self.render[x][y].text ~= false then
term.write(self.render[x][y].text)
else
term.write(" ")
end
end
end
end
end
function entity:undraw()
local sizeX, sizeY = self:getSize()
for x = 1, sizeX, 1 do
for y = 1, sizeY, 1 do
if self.render[x][y].color ~= false or self.render[x][y].text ~= false then
term.setCursorPos(x + (self.posX - 1), y + (self.posY - 1))
term.setBackgroundColor(backgrounds.backgroundlist[backgrounds.selected].render[x + (self.posX - 1)][y + (self.posY - 1)].color)
term.write(" ")
end
end
end
end
function entity:kill()
self:undraw()
t_removeItem(self.parent, self)
self:destroy()
end
function entity:collision(target)
local lastX, lastY = self:getLastPosition()
local tLastX, tLastY = target:getLastPosition()
local sizeX, sizeY = self:getSize()
local tSizeX, tSizeY = target:getSize()
for x = self.posX, lastX, 1 do
for y = self.posY, lastY, 1 do
for tx = target.posX, tLastX, 1 do
for ty = target.posY, tLastY, 1 do
if x == tx and y == ty and (self.render[x - (self.posX - 1)][y - (self.posY - 1)].color ~= false or self.render[x - (self.posX - 1)][y - (self.posY - 1)].text ~= false) and (target.render[tx - (target.posX - 1)][ty - (target.posY - 1)].color ~= false or target.render[tx - (target.posX - 1)][ty - (target.posY - 1)].text ~= false) then
return true
end
end
end
end
end
end
entity = class_lib.class:new(entity)
local background = { render = false }
function background:draw()
for x = 1, 51, 1 do
for y = 1, 19, 1 do
term.setCursorPos(x, y)
if self.render[x][y].color ~= false then
term.setBackgroundColor(self.render[x][y].color)
else
term.setBackgroundColor(colours.black)
end
if self.render[x][y].text ~= false then
term.write(self.render[x][y].text)
else
term.write(" ")
end
end
end
end
background = class_lib.class:new(background)
local menu = { selection = {}, selected = 1 }
function menu:draw(posX, posY)
local my_posY, my_posX = posY, posX
for index, point in pairs(self.selection) do
term.setCursorPos(my_posX, my_posY)
if point[1][1].color ~= false then
term.setBackgroundColor(point[1][1].color)
else
term.setBackgroundColor(backgrounds.backgroundlist[backgrounds.selected].render[my_posX][my_posY].color)
end
if index == self.selected then
term.setTextColor(colours.green)
end
if point[1][1].text ~= false then
term.write(point[1][1].text)
else
term.write("Default Text")
end
term.setTextColor(colours.black)
my_posY = my_posY + 2
end
end
function menu:up()
if self.selected > 1 then
self.selected = self.selected - 1
else
self.selected = #self.selection
end
end
function menu:down()
if self.selected < #self.selection then
self.selected = self.selected + 1
else
self.selected = 1
end
end
menu = class_lib.class:new(menu)
local controller = { finished = false, keys = {} }
function controller:start(p_func)
self.finished = false
while self.finished == false do
parallel.waitForAny(function() self:get_key() end, p_func)
end
end
function controller:get_key()
while self.finished == false do
local _, key = os.pullEvent("key")
for index, data in pairs(self.keys) do
if data.key == key then
data.func()
end
end
end
end
function controller:stop()
self.finished = true
end
controller = class_lib.class:new(controller)
function t_getIndex(table, item)
for index, value in pairs(table) do
if item == value then
return index
end
end
return false
end
function t_removeIndex(table, index)
local new_table = {}
for i, value in pairs(table) do
if i ~= index then
if type(i) == "number" then
new_table[#new_table + 1] = table[i]
else
new_table[i] = table[i]
end
end
end
table = new_table
return table
end
function t_removeItem(table, item)
local index = t_getIndex(table, item)
return t_removeIndex(table, index)
end
function add_player(posX, posY, name)
players[name] = entity:new({ posX = posX, posY = posY, render = render:new(), parent = players })
players[name].render:init()
return players[name]
end
function add_enemy(posX, posY, name)
enemys[name] = entity:new({ posX = posX, posY = posY, render = render:new(), parent = enemys })
enemys[name].render:init()
return enemys[name]
end
function add_other(posX, posY, name)
others[name] = entity:new({ posX = posX, posY = posY, render = render:new(), parent = others })
others[name].render:init()
return others[name]
end
function add_background(name)
backgrounds.backgroundlist[name] = background:new({ render = render:new() })
backgrounds.backgroundlist[name].render:init()
return backgrounds.backgroundlist[name]
end
function add_menu(name)
menus.menulist[name] = menu:new()
return menus.menulist[name]
end
function add_menu_point(target, text, color)
local n_render = render:new()
n_render:init()
local index = t_getIndex(menus.menulist, target)
n_render[1][1].text = text
if color ~= nil then
n_render[1][1].color = color
end
menus.menulist[index].selection[#menus.menulist[index].selection + 1] = n_render
return menus.menulist[index].selection[#menus.menulist[index].selection]
end
function change_background(target)
local index = t_getIndex(backgrounds.backgroundlist, target)
backgrounds.selected = index
backgrounds.backgroundlist[index]:draw()
end
function change_menu(target, posX, posY)
local index = t_getIndex(menus.menulist, target)
menus.selected = index
menus.menulist[index]:draw(posX, posY)
end
function add_controller(name)
controllers[name] = controller:new()
return controllers[name]
end
function add_controller_key(target, name, key, func)
local index = t_getIndex(controllers, target)
controllers[index].keys[name] = { key = key, func = func }
return controllers[index].keys[name]
end
return { t_getIndex = t_getIndex, t_removeIndex = t_removeIndex, t_removeItem = t_removeItem, add_player = add_player, add_enemy = add_enemy, add_other = add_other, add_background = add_background, add_menu = add_menu, add_menu_point = add_menu_point, change_background = change_background, change_menu = change_menu, add_controller = add_controller, add_controller_key = add_controller_key }
Test Programm:
if fs.exists("game-utils") == false then shell.run("wget https://raw.githubusercontent.com/Terandox-The-Pineapple/TRX-Librarys/dev/game-utils.lua game-utils") end
local game_utils = require("game-utils")
math.randomseed(os.time())
local my_back = game_utils.add_background("my_back")
for x = 1, 51, 1 do
for y = 1, 19, 1 do
if x == 1 or x == 51 or y == 1 or y == 19 then
my_back.render[x][y].color = colours.green
else
my_back.render[x][y].color = colours.red
end
end
end
game_utils.change_background(my_back)
local player = game_utils.add_player(2,2,"player_1")
local enemy = game_utils.add_enemy(50,18,"enemy_1")
local buffer = false
player.render[2][1].color = colours.black
player.render[2][2].color = colours.black
player.render[2][3].color = colours.black
player.render[1][2].color = colours.black
player.render[3][2].color = colours.black
enemy.render[1][1].color = colours.pink
local controlls = game_utils.add_controller("controlls")
game_utils.add_controller_key(controlls, "Quit", keys.q, function()
controlls:stop()
term.setBackgroundColor(colors.black)
shell.run("clear")
print("Programm finished")
end)
game_utils.add_controller_key(controlls, "Up", keys.up, function()
buffer = "U"
end)
game_utils.add_controller_key(controlls, "Down", keys.down, function()
buffer = "D"
end)
game_utils.add_controller_key(controlls, "Left", keys.left, function()
buffer = "L"
end)
game_utils.add_controller_key(controlls, "Right", keys.right, function()
buffer = "R"
end)
local e_move = "L"
local bullets = {}
player:draw()
function game()
os.sleep(0.01)
if enemy:collision(player) then hit() end
enemy:undraw()
if e_move == "R" then
enemy:moveRight()
if enemy.posX >= 50 then
e_move = "L"
end
elseif e_move == "L" then
enemy:moveLeft()
if enemy.posX <= 2 then
e_move = "R"
end
else
controlls:stop()
end
enemy:draw()
if enemy:collision(player) then hit() end
for index, bullet in pairs(bullets) do
bullet:fly()
end
test_shot()
local sizeX, sizeY = player:getSize()
if buffer == "U" and player.posY > 2 then
player:undraw()
player:moveUp()
player:draw()
elseif buffer == "D" and player.posY < (19 - sizeY) then
player:undraw()
player:moveDown()
player:draw()
elseif buffer == "L" and player.posX > 2 then
player:undraw()
player:moveLeft()
player:draw()
elseif buffer == "R" and player.posX < (51 - sizeX) then
player:undraw()
player:moveRight()
player:draw()
end
end
function test_shot()
local value = math.random(1,5)
if value == 1 then shoot() end
end
function shoot()
local bullet = game_utils.add_other(enemy.posX,enemy.posY-1, #bullets+1)
function bullet:fly()
if self.posY > 2 then
if self:collision(player) then hit() end
self:undraw()
self:moveUp()
self:draw()
if self:collision(player) then hit() end
else
if self:collision(player) then hit() end
game_utils.t_removeItem(bullets,self)
self:kill()
end
end
bullet.render[1][1].color = colours.purple
bullets[#bullets+1] = bullet
end
function hit()
controlls:stop()
term.setBackgroundColor(colors.black)
shell.run("clear")
print("Hit")
return true
end
controlls:start(game)
Link to Test Programm (Installs dependencys automatic on start): https://pastebin.com/y2GjKDJ7 (https://pastebin.com/y2GjKDJ7)
The test programm runs normal at first and then gets slower each time a bullet is shot.
After a short time the programm than crashes with this error:
Quoteclass:32: To long without yielding
I have no idea what i did wrong and hope that someone can help me out.
After a lot of headscratching i managed to locate the bug and removed it.
The problem was that render:init() cloned the entire render class 1 times inside the key it should be and 1 times outside of it.
Also i created all bullets with add_other() function. I now create 1 class with add_other() function and the rests out if this one with :new() function.
Finaly it works... hurray.
The final version of my test programm can be obtained with the link in the main post.
The final versions of my librarys can be obtained on my github.
wish you a nice day