前口上
前回の外部クラス化を用いて、spriteの種類ごとにコライダーの衝突処理を変更してみます。
本記事は以下リポジトリを参考に制作しています。
実装
プレイヤーが矢を射出し、壁に矢がぶつかると反射していくという内容のものを作ります。
プレイヤー、壁、弓矢の3種類のクラスを用意し、クラスを判別してそれぞれのコライダーの衝突処理を実装します。
それぞれ source
ディレクトリ直下に配置しています。
local gfx <const> = playdate.graphics
class('Player').extends(gfx.sprite)
function Player:init(x, y)
Player.super.init(self)
local playerImage = gfx.image.new("Images/playerImage")
assert( playerImage )
self:setImage( playerImage )
self:moveTo( x, y )
self:setCollideRect(0, 0, self:getSize())
self.collisionResponse = playdate.graphics.sprite.kCollisionTypeBounce
end
function Player:update()
local offsetX = 0
local offsetY = 0
if playdate.buttonIsPressed( playdate.kButtonUp ) then
offsetY = -2
end
if playdate.buttonIsPressed( playdate.kButtonRight ) then
offsetX = 2
end
if playdate.buttonIsPressed( playdate.kButtonDown ) then
offsetY = 2
end
if playdate.buttonIsPressed( playdate.kButtonLeft ) then
offsetX = -2
end
self:moveWithCollisions(self.x + offsetX, self.y + offsetY)
end
function Player:collisionResponse(other)
if other:isa(Wall) then
return playdate.graphics.sprite.kCollisionTypeFreeze
else
return playdate.graphics.sprite.kCollisionTypeOverlap
end
end
local gfx <const> = playdate.graphics
class('Wall').extends(gfx.sprite)
function Wall:init(x, y, w, h)
Wall.super.init(self)
local wallImage = gfx.image.new(w, h)
gfx.pushContext(wallImage)
gfx.setColor(gfx.kColorWhite)
gfx.fillRect(0, 0, w, h)
gfx.popContext()
self:setImage(wallImage)
self:moveTo(x, y)
self:setCollideRect(0, 0, self:getSize())
end
import "CoreLibs/object"
import "CoreLibs/sprites"
local gfx <const> = playdate.graphics
class('Arrow').extends(gfx.sprite)
function Arrow:init(x, y, speed, accel)
Arrow.super.init(self)
local arrowImage = gfx.image.new("Images/arrow")
self:setImage(arrowImage)
self:moveTo(x, y)
self:setCollideRect(0, 4, 12, 5)
self.speed = speed
self.accel = accel
self.currentSpeed = speed
self.minSpeed = speed / 2
self.yOffset = math.random(-10,10) / 10
self.dir = 1
end
function Arrow:update()
Arrow.super.update(self)
self:moveWithCollisions(self.x + self.dir * self.currentSpeed, self.y + self.yOffset)
self.currentSpeed -= self.dir * self.accel
if math.abs(self.currentSpeed) < self.minSpeed then
self.currentSpeed = self.dir * self.minSpeed
end
end
function Arrow:collisionResponse(other)
if other:isa(Wall) then
self.dir *= -1
self:setRotation(180)
return playdate.graphics.sprite.kCollisionTypeBounce
else
return playdate.graphics.sprite.kCollisionTypeOverlap
end
end
上記をmain.lua
で読み込みます。
矢の射出はAボタンを押すと実行されるようにしました。
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"
import "arrow"
import "wall"
import "player"
local gfx <const> = playdate.graphics
local playerSprite = nil
local wallSprite = nil
function myGameSetUp()
playerSprite = Player(200, 120)
playerSprite:add()
wallSprite = Wall(300, 120, 20, 80)
wallSprite:add()
local backgroundImage = gfx.image.new( "Images/background" )
assert( backgroundImage )
gfx.sprite.setBackgroundDrawingCallback(
function( x, y, width, height )
backgroundImage:draw( 0, 0 )
end
)
end
myGameSetUp()
function playdate.update()
-- 矢を射出
if playdate.buttonIsPressed(playdate.kButtonA) then
local newArrow = Arrow(playerSprite.x, playerSprite.y, 10, .1)
newArrow:add()
end
gfx.sprite.update()
playdate.timer.updateTimers()
end
解説
ポイントとしては動くスプライト(player、arrow)のsprite:collisionResponse(other)
で、衝突時の処理を実行します。
other
が衝突したオブジェクトで、other:isa([クラス名])
で指定したクラス名に衝突したかを調べています。
collisionResponseの返り値は衝突したときの振る舞いの定数が返ってきています。
あとはmain.lua
で初期状態でplayerとwallを出し、update関数のplaydate.buttonIsPressed(playdate.kButtonA)
の中でAボタンが押されたとき、矢を射出するよう処理を記述してます。