300 lines
7.4 KiB
Lua
300 lines
7.4 KiB
Lua
package.path = package.path..";"..RL.GetBasePath().."../resources/lib/?.lua"
|
|
|
|
Util = require( "utillib" )
|
|
Vec2 = require( "vector2" )
|
|
|
|
local TILE_SIZE = 16
|
|
local PLAYER_MAXSPEED = 1.5
|
|
local PLAYER_ACCELL = 5
|
|
local PLAYER_DEACCELL = 5
|
|
local GRAVITY = 6
|
|
local JUMP_STR = 3
|
|
local WALK_ANIM_SPEED = 12
|
|
|
|
local tex = RL.LoadTexture( RL.GetBasePath().."../resources/images/arcade_platformerV2.png" )
|
|
local res = Vec2:new( 160, 144 )
|
|
local winScale = 5
|
|
local winSize = res:scale( winScale )
|
|
local framebuffer = RL.LoadRenderTexture( res )
|
|
local monitor = 0
|
|
local tilemap = {
|
|
size = Vec2:new( res.x / TILE_SIZE, res.y / TILE_SIZE ),
|
|
tiles = {},
|
|
tileRects = {
|
|
{ 224, 112, TILE_SIZE, TILE_SIZE },
|
|
{ 224, 48, TILE_SIZE, TILE_SIZE },
|
|
{ 208, 48, TILE_SIZE, TILE_SIZE },
|
|
{ 240, 48, TILE_SIZE, TILE_SIZE },
|
|
{ 256, 96, TILE_SIZE, TILE_SIZE },
|
|
{ 256, 112, TILE_SIZE, TILE_SIZE },
|
|
},
|
|
}
|
|
|
|
local player = {
|
|
vel = Vec2:new( 0, 0 ),
|
|
pos = Vec2:new( 32, 32 ), -- Center bottom.
|
|
colRect = { 0, 0, 12, 14 },
|
|
onFloor = false,
|
|
frames = {
|
|
{ 6, 14, 20, 18 },
|
|
{ 39, 13, 20, 19 },
|
|
{ 70, 14, 20, 18 },
|
|
{ 6, 45, 20, 18 },
|
|
{ 38, 45, 20, 18 },
|
|
{ 70, 45, 20, 18 },
|
|
},
|
|
walkAnimFrames = { 2, 3, 4, 3 },
|
|
curFrame = 1,
|
|
animPos = 0.0,
|
|
facing = 1,
|
|
}
|
|
|
|
local function createMap()
|
|
for x = 1, tilemap.size.x do
|
|
table.insert( tilemap.tiles, {} )
|
|
|
|
for y = 1, tilemap.size.y do
|
|
if y == tilemap.size.y then
|
|
table.insert( tilemap.tiles[x], 1 )
|
|
else
|
|
table.insert( tilemap.tiles[x], 0 )
|
|
end
|
|
end
|
|
end
|
|
|
|
tilemap.tiles[4][7] = 3
|
|
tilemap.tiles[5][7] = 2
|
|
tilemap.tiles[6][7] = 2
|
|
tilemap.tiles[7][7] = 4
|
|
tilemap.tiles[6][6] = 5
|
|
|
|
tilemap.tiles[10][4] = 5
|
|
tilemap.tiles[10][5] = 6
|
|
tilemap.tiles[10][6] = 6
|
|
tilemap.tiles[10][7] = 6
|
|
tilemap.tiles[10][8] = 6
|
|
|
|
tilemap.tiles[1][5] = 5
|
|
tilemap.tiles[1][6] = 6
|
|
tilemap.tiles[1][7] = 6
|
|
tilemap.tiles[1][8] = 6
|
|
end
|
|
|
|
function RL.init()
|
|
local monitorPos = Vec2:new( RL.GetMonitorPosition( monitor ) )
|
|
local monitorSize = Vec2:new( RL.GetMonitorSize( monitor ) )
|
|
|
|
RL.SetWindowTitle( "Platformer" )
|
|
RL.SetWindowState( RL.FLAG_WINDOW_RESIZABLE )
|
|
RL.SetWindowState( RL.FLAG_VSYNC_HINT )
|
|
RL.SetWindowSize( winSize )
|
|
RL.SetWindowPosition( { monitorPos.x + monitorSize.x / 2 - winSize.x / 2, monitorPos.y + monitorSize.y / 2 - winSize.y / 2 } )
|
|
|
|
createMap()
|
|
end
|
|
|
|
local function isTileWall( pos )
|
|
if RL.CheckCollisionPointRec( { pos.x, pos.y }, { 0, 0, tilemap.size.x - 1, tilemap.size.y - 1 } ) then
|
|
return 0 < tilemap.tiles[ pos.x + 1 ][ pos.y + 1 ]
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function tileCollision( entity )
|
|
local vPos = entity.pos + entity.vel -- Future pos with current vel.
|
|
local vRect = Util.tableClone( entity.colRect )
|
|
local tinyGap = 0.001 -- Tiny gap between collisionRect and tile to prevent getting stuck on all seams.
|
|
|
|
-- Move test rect to predicted position.
|
|
vRect[1] = vPos.x - vRect[3] / 2
|
|
|
|
-- Tile range where collision box is affecting.
|
|
local tileRect = {
|
|
math.floor( vRect[1] / TILE_SIZE ),
|
|
math.floor( vRect[2] / TILE_SIZE ),
|
|
math.floor( ( vRect[1] + vRect[3] ) / TILE_SIZE ),
|
|
math.floor( ( vRect[2] + vRect[4] ) / TILE_SIZE ),
|
|
}
|
|
|
|
for y = tileRect[2], tileRect[4] do
|
|
if 0 < entity.vel.x then
|
|
if isTileWall( Vec2:new( tileRect[3], y ) ) then
|
|
-- Use new_x to push out of tile.
|
|
local new_x = tileRect[3] * TILE_SIZE - ( entity.colRect[1] + entity.colRect[3] )
|
|
|
|
entity.vel.x = new_x - tinyGap
|
|
|
|
break
|
|
end
|
|
elseif entity.vel.x < 0 then
|
|
if isTileWall( Vec2:new( tileRect[1], y ) ) then
|
|
local new_x = ( tileRect[1] * TILE_SIZE + TILE_SIZE ) - entity.colRect[1]
|
|
entity.vel.x = new_x + tinyGap
|
|
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Calculate new tileRect for y.
|
|
vRect[1] = entity.colRect[1] -- Reset to non predicted one.
|
|
vRect[2] = vPos.y - vRect[4]
|
|
|
|
tileRect = {
|
|
math.floor( vRect[1] / TILE_SIZE ),
|
|
math.floor( vRect[2] / TILE_SIZE ),
|
|
math.floor( ( vRect[1] + vRect[3] ) / TILE_SIZE ),
|
|
math.floor( ( vRect[2] + vRect[4] ) / TILE_SIZE ),
|
|
}
|
|
|
|
for x = tileRect[1], tileRect[3] do
|
|
if 0 < entity.vel.y then
|
|
if isTileWall( Vec2:new( x, tileRect[4] ) ) then
|
|
local new_y = tileRect[4] * TILE_SIZE - ( entity.colRect[2] + entity.colRect[4] )
|
|
-- math.max prevents bounce when hitting right on the corner.
|
|
entity.vel.y = math.max( new_y - tinyGap, 0 )
|
|
player.onFloor = true
|
|
|
|
break
|
|
end
|
|
elseif entity.vel.y < 0 then
|
|
if isTileWall( Vec2:new( x, tileRect[2] ) ) then
|
|
local new_y = ( tileRect[2] * TILE_SIZE + TILE_SIZE ) - entity.colRect[2]
|
|
entity.vel.y = new_y + tinyGap
|
|
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function playerMovement( delta )
|
|
local moving = { false, false }
|
|
|
|
if RL.IsKeyDown( RL.KEY_RIGHT ) then
|
|
player.vel.x = player.vel.x + PLAYER_ACCELL * delta
|
|
moving[1] = true
|
|
|
|
if 0 < player.vel.x then
|
|
player.facing = 1
|
|
end
|
|
elseif RL.IsKeyDown( RL.KEY_LEFT ) then
|
|
player.vel.x = player.vel.x - PLAYER_ACCELL * delta
|
|
moving[1] = true
|
|
|
|
if player.vel.x < 0 then
|
|
player.facing = -1
|
|
end
|
|
end
|
|
|
|
if RL.IsKeyPressed( RL.KEY_SPACE ) and player.onFloor then
|
|
player.vel.y = -JUMP_STR
|
|
player.onFloor = false
|
|
end
|
|
|
|
-- Deaccel.
|
|
|
|
if not moving[1] then
|
|
if delta * PLAYER_DEACCELL < player.vel.x then
|
|
player.vel.x = player.vel.x - PLAYER_DEACCELL * delta
|
|
elseif player.vel.x < -delta * PLAYER_DEACCELL then
|
|
player.vel.x = player.vel.x + PLAYER_DEACCELL * delta
|
|
else
|
|
player.vel.x = 0.0
|
|
end
|
|
end
|
|
|
|
player.vel.x = Util.clamp( player.vel.x, -PLAYER_MAXSPEED, PLAYER_MAXSPEED )
|
|
|
|
player.vel.y = player.vel.y + GRAVITY * delta
|
|
|
|
-- Drop from platform.
|
|
if player.onFloor and 0.5 < player.vel.y then
|
|
player.onFloor = false
|
|
end
|
|
|
|
tileCollision( player )
|
|
player.pos = player.pos + player.vel
|
|
player.colRect[1] = player.pos.x - player.colRect[3] / 2
|
|
player.colRect[2] = player.pos.y - player.colRect[4]
|
|
end
|
|
|
|
function RL.process( delta )
|
|
if RL.IsWindowResized() then
|
|
winSize:set( RL.GetScreenSize() )
|
|
end
|
|
|
|
playerMovement( delta )
|
|
end
|
|
|
|
local function drawMap()
|
|
RL.DrawTextureRec( tex, { 0, 160, res.x, res.y }, { 0, 0 }, RL.WHITE )
|
|
|
|
for x = 1, tilemap.size.x do
|
|
for y = 1, tilemap.size.y do
|
|
local tile = tilemap.tiles[x][y]
|
|
local pos = Vec2:new( x - 1, y - 1 )
|
|
|
|
if 0 < tile then
|
|
RL.DrawTextureRec( tex, tilemap.tileRects[ tile ], { pos.x * TILE_SIZE, pos.y * TILE_SIZE }, RL.WHITE )
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function drawPlayer()
|
|
-- Animation.
|
|
|
|
if player.onFloor then
|
|
if math.abs( player.vel.x ) < 0.1 then
|
|
player.curFrame = 1
|
|
else
|
|
player.animPos = player.animPos + WALK_ANIM_SPEED * ( math.abs( player.vel.x ) / PLAYER_MAXSPEED ) * RL.GetFrameTime()
|
|
local frame = math.ceil( player.animPos )
|
|
|
|
if #player.walkAnimFrames < frame then
|
|
frame = 1
|
|
player.animPos = player.animPos - #player.walkAnimFrames
|
|
end
|
|
|
|
player.curFrame = frame
|
|
end
|
|
else
|
|
if 0 < player.vel.y then
|
|
player.curFrame = 6
|
|
elseif player.vel.y < 0 then
|
|
player.curFrame = 5
|
|
end
|
|
end
|
|
|
|
-- Draw rect.
|
|
|
|
local src = Util.tableClone( player.frames[ player.curFrame ] )
|
|
local dst = {
|
|
player.pos.x - src[3] / 2,
|
|
player.pos.y - src[4],
|
|
src[3],
|
|
src[4],
|
|
}
|
|
|
|
if player.facing < 0 then
|
|
src[3] = -src[3]
|
|
end
|
|
|
|
RL.DrawTexturePro( tex, src, dst, { 0, 0 }, 0.0, RL.WHITE )
|
|
end
|
|
|
|
function RL.draw()
|
|
RL.ClearBackground( RL.RED )
|
|
|
|
RL.BeginTextureMode( framebuffer )
|
|
drawMap()
|
|
drawPlayer()
|
|
RL.EndTextureMode()
|
|
|
|
RL.DrawTexturePro( framebuffer, { 0, 0, res.x, -res.y }, { 0, 0, winSize.x, winSize.y }, { 0, 0 }, 0.0, RL.WHITE )
|
|
|
|
-- RL.glBlitFramebuffer( framebuffer, -1, res, winSize, RL.GL_COLOR_BUFFER_BIT, RL.GL_NEAREST )
|
|
end
|