This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Exerro's profile picture

Yasuda Game Engine - Collision detection and camera locking

Started by Exerro, 09 March 2013 - 09:02 AM
Exerro #1
Posted 09 March 2013 - 10:02 AM
Yasuda Game Engine!
Latest update: camera locking & bug fixes

Yasuda is a game engine I have been working on for about a week now. It is designed to be easy to use, and open but have add-ons that specialize it to make it better for certain tasks. The main engine has collision detection but the map addon uses auto collision and will eventually have things like following and attacking in built ( so you can set entities to attack/follow other entities automatically )

Well, how good is it really? Here's an example:
Spoiler


game.loadAddon( "map.gma" ) -- loads the map addon
m = game.add( 1, 1, 6, 9, "player", "player" ) -- creates the player entity
map = game.add( 1, 1, 51, 18, "a", "map" ) --creates the map entity
t = game.loadMap( true, true ) -- creates a map with pixel perfect collision and checks before it moves the player
m:insertToMap( t ) -- inserts the player into the map
map:insertToMap( t ) -- inserts the map entity into the map
while true do
term.setBackgroundColour( colours.lime )
term.clear( ) -- clears the screen with a lime background
map:draw( ) -- draws the map
m:draw( ) -- draws the player
ev = { os.pullEvent( ) } -- gets the event
if ev[1] == "key" then
  if ev[2] == keys.left then
   m:move( -1, 0, t ) -- moves the player to the left ( checks collision while doing so automatically with the map addon )
  elseif ev[2] == keys.right then
   m:move( 1, 0, t ) -- moves to the right
  elseif ev[2] == keys.up then
   m:move( 0, -1, t ) -- moves up
  elseif ev[2] == keys.down then
   m:move( 0, 1, t ) -- moves down
  elseif ev[2] == keys.backspace then
   break -- exits the program
  elseif ev[2] == keys.tab then
   if m:isLockedEntity( ) then -- checks if the camera is locked to the player
	map:lock( ) -- locks the camera to the map
   else
	m:lock( ) -- locks the camera to the player
   end
  end
end
end
This would create a lime screen with a map drawn on that will not let the player move through bits of the map that aren't invisible. You can press tab to switch view to the player or a side view of the whole map


What is so good about it?
-Easy to make add-ons for/overwrite current ones to suit your needs
-Per pixel and rectangle collision detection
-Entity storage and management
-Addons:
–Map - auto collision
–RPG - extra variables & functions related to role playing games such as money and health & collision attacking


Still to come:
entity actions such as following, attacking, eating and mining
terrain add-on that lets you make a 2d minecraft style terrain
animation support ( either .nfa or one I will make or both )

What do I need to know?
to load all addons use game.loadAddons( ) or game.loadAddon( path ) to load individually.
to create an entity use game.add( x, y, w, h, image, name ( used for map collision ) )
to make an addon create a file and make a table called game.addon ( these will be inserted into the game table.
to check collision do rec, pix = [entity]:isColliding( [entity], [per pixel collision?] )
to move and entity use [entity]:move( x, y ) or [entity]:teleport( x, y )

How can I get it?
pastebin: PiA7Sw5T <– latest version
Spoiler



game = {

add = function( x, y, w, h, image, name, actions, desc )
local actions = actions or { }
local desc = desc or { }
local t = { oldX = x, oldY = y, dimension = "default", x = x, y = y, w = w, h = h, image = ( type( image ) == "string" and game.loadImage( image ) or image ), actions = actions, desc = desc, name = name }
return setmetatable( t, { __index = game } )
end;

loadImage = function( path )
--loads the image in a specific format
local t = { }
local tImage = paintutils.loadImage( path )
for y=1,#tImage do
local tLine = tImage[y]
for x=1,#tLine do
if tLine[x] > 0 then
table.insert( t, { x = x, y = y, bc = tLine[x], text = " ", tc = 1 } )
end
end
end
local maxX = 0
local maxY = 0
for i = 1,#t do
if t[i].x > maxX then
maxX = t[i].x
end
if t[i].y > maxY then
maxY = t[i].y
end
end
local r = { }
for i = 1,maxY do
r[i] = { }
for k = 1,maxX do
r[i][k] = { text = false, bc = 2, tc = 2 }
end
end
for i = 1,#t do
r[t[i].y][t[i].x] = { text = t[i].text, bc = t[i].bc, tc = t[i].tc }
end
return r
end;

setDimension = function( self, dim )
--changes the entities dimension ( for collision )
self.dimension = dim
return self.dimension
end;

camera = { entity = { x = 1, y = 1, w = 51, h = 19 } };

lock = function( self )
game.camera.entity = self
end;

draw = function( self )
--draws the entity at the entities x and y
for y = 1,#self.image do
for x = 1,#self.image[y] do
im = self.image[y][x]
if im.text ~= false then
local xdiff = ( self.x - game.camera.entity.x ) + ( 26 - ( game.camera.entity.w / 2 ) )
local ydiff = ( self.y - game.camera.entity.y ) + ( 10 - ( game.camera.entity.h / 2 ) )
term.native.setCursorPos( x + xdiff, y + ydiff )
term.native.setBackgroundColour( im.bc )
term.native.setTextColour( im.tc )
term.native.write( im.text )
end
end
end
end;

isLockedEntity = function( self )
if game.camera.entity == self then
return true
end
return false
end;

loadAddons = function( path )
path = path or ""
local f = fs.list( path )
for i = 1,#f do
if fs.isDir( f[i] ) then
game.loadAddons( f[i] )
elseif string.sub( f[i], string.len( f[i] )-3, string.len( f[i] ) ) == ".gma" then 
game.loadAddon( f[i] )
end
end
end;

move = function( self, x, y )
--changes the entities x and y to x+x and y+y
self.oldX, self.oldY = self.x, self.y
self.x = self.x + math.floor( x )
self.y = self.y + math.floor( y )
end;

teleport = function( self, x, y )
--sets the entities x and y to x and y
self.oldX, self.oldY = self.x, self.y
self.x, self.y = x, y
end;

pixel = function( self, x, y, text, bc, tc )
if not self.image[y][x] then return end
bc = bc or self.image[y][x].bc-- or colours.black
tc = tc or self.image[y][x].tc-- or colours.white
text = text or self.image[y][x].item-- or " "
self.image[y][x] = { text = text, bc = bc, tc = tc }
self:draw( )
end;

remove = function( self, x, y )
if not self.image[y][x] then return end
self.image[y][x] = 
{ text = false, bc = colours.white, tc = colours.white }
end;

getOppositeEdge = function( self, y )
if y then
return self.y+( self.h-1 )
end
return self.x+( self.w-1 )
end;

loadAddon = function( path )
--will load addons
local loaded = true
parallel.waitForAny( function( ) shell.run( path, "game_API_is_adding_you" ) end, function( ) sleep( 1 ) loaded = false end )
if loaded and game.addon then
for k, v in pairs( game.addon ) do
game[k] = v
end
end
game.addon = nil
return loaded
end;

back = function( self )
self:teleport( self.oldX, self.oldY )
end;

collide = function( r1, r2 )

end;

isColliding = function( r1, r2, detail )
if r2 == "screen" then
if r1.x < 1 or
r1.y < 1 or
r1:getOppositeEdge( ) > 51 or
r1:getOppositeEdge( true ) > 19 then
return true
end
return false
end
if r1.dimension ~= r2.dimension then
return false, false
end
local left = false
local top = false
local l, r, t, b = nil, nil, nil, nil
if r1.x < r2.x then
l, r = r2.x, r1:getOppositeEdge( )
left = true
elseif r1.x >= r2.x then
l, r = r1.x, r2:getOppositeEdge( )
end
if r1.y < r2.y then
t, b = r2.y, r1:getOppositeEdge( true )
top = true
elseif r1.y >= r2.y then
t, b = r1.y, r2:getOppositeEdge( true )
end
if l > r or t > b then
return false, false
end
if not detail then game.collide( r1, r2 ) return true, false end
for x = l, r do
for y = t, b do
local r1x, r2x, r1y, r2y = nil, nil, nil, nil
r1x, r2x = x-r1.x+1, x-r2.x+1
r1y, r2y = y-r1.y+1, y-r2.y+1
if r1.image[r1y] and r1.image[r1y][r1x] and r1.image[r1y][r1x].text ~= false and 
r2.image[r2y] and r2.image[r2y][r2x] and r2.image[r2y][r2x].text ~= false then
game.collide( r1, r2 )
return true, true
end
end
end
   game.collide( r1, r2 )
return true, false
end;

}

map addon:
Spoiler


local args = { ... }
if args[1] ~= "game_API_is_adding_you" then
return
end
game.addon = {

loadMap = function( col, mov )
  game.map = { }
  game.map.col = col
  game.map.mov = mov
  local t = { }
  return t
end;

setCollision = function( col )
  game.map.col = col
end;

setMoveCollision = function( tf )
  game.map.mov = tf
end;

insertToMap = function( self, t )
  table.insert( t, self )
end;

move = function( self, x, y, map )
  --changes the entities x and y to x+x and y+y
  self.oldX, self.oldY = self.x, self.y
  self.x = self.x+x
  if game.map.mov then
   for i = 1,#map do
	if map[i].name ~= self.name and map[i].name then
	 rec, pix = self:isColliding( map[i], true )
	 if game.map.col and pix then
	  self.x = self.x-x
	  break
	 elseif rec and not game.map.col then
	  self.x = self.x-x
	  break
	 end
	end
   end
  end
  self.y = self.y+y
  if game.map.mov then
   for i = 1,#map do
	if map[i].name ~= self.name then
	 rec, pix = self:isColliding( map[i], true )
	  if game.map.col and pix then
	  self.y = self.y-y
	  break
	 elseif rec and not game.map.col then
	  self.y = self.y-y
	  break
	 end
	end
   end
  end
end;
}

RPG addon: coming soon
superaxander #2
Posted 10 March 2013 - 07:50 PM
Cool and awesome
Exerro #3
Posted 11 March 2013 - 12:39 AM
Thanks :D/> I will be adding in loads more addons soon that will hopefully make things more suited to your needs. If you have any suggestions of an addon to add in just tell me and I will get right on it!
NeptunasLT #4
Posted 11 March 2013 - 12:55 AM
Getting Black Screen :(/>
Exerro #5
Posted 11 March 2013 - 01:04 AM
:(/> can you pm me your code? you have to draw the entities with [entity]:draw( )
NeptunasLT #6
Posted 11 March 2013 - 01:32 AM
Downloaded the file as "s" then run "s" and i get black screen :(/>
Exerro #7
Posted 11 March 2013 - 01:35 AM
It's an API not a program, it won't execute any code…if you downloaded the example program then you have to make images called player and a
Papa_Beans #8
Posted 26 November 2013 - 10:12 AM
Okay, I downloaded the game API. I also downloaded the map API as map.gma. Then I creates a simple char[named player], and a simple map(black line)[named a]. Whenever I try to run it I get game:15: attempt to get length of nil . Why is this?
Exerro #9
Posted 29 November 2013 - 01:41 PM
although i stopped work on this as while ago, its something going wrong when you try and load an image… make sure that all the images you try to load are formatted right