SDK Version: 1.13.2(Mac)
I'm making a shoot 'em up game.
I'm currently struggling with optimization. Please help me.
I want to keep a stable frame rate while keeping as many enemy planes and bullets on screen as possible.
Any tips for this?
SDK Version: 1.13.2(Mac)
I'm making a shoot 'em up game.
I'm currently struggling with optimization. Please help me.
I want to keep a stable frame rate while keeping as many enemy planes and bullets on screen as possible.
Any tips for this?
If you are coding in Lua, this thread includes some hints you can use to optimize your shoot'em up:
Thank you Daeke! I'll check it.
Your comment is too helpful to me.
Would using an image table help improve frames?
I tried using an image table as a test, but it doesn't seem to have changed much whether I'm using it incorrectly.
If you have any experience using image tables, does anyone have any stories to tell?
I'ts hard to say without knowing more about how you're using the API, but if you're already using images I don't think you'd see any difference with image tables--they're just images packed together in a single file. The first thing you'll want to do is sample the game with the profiler and see what code is taking the most CPU. It looks like there's some talk about that in the thread that Daeke posted, but let me know if you need any help with that!
I'm testing it by deploying 10 enemy planes and changing what I update.
Whenever the update items increase, the proportion of games in device info increases exponentially.
Would you give me some advice?
Here's what the update checks for:
And This is my code
import "player"
import 'gameManager/soundManager'
local pd <const> = playdate
local gfx <const> = pd.graphics
class('Enemy').extends(gfx.sprite)
local imageTable = gfx.imagetable.new('images/base')
-- local animationLoop = gfx.animation.loop.new(100, imageTable)
local enemyImages = {}
for i = 1, 2 do
enemyImages[i] = imageTable[i + 18]
end
local dieImages = {}
for i = 1, 5 do
dieImages[i] = imageTable[i + 31]
end
function Enemy:init()
self.enemyImages = enemyImages
self.dieImages = dieImages
--"basic", "driller", "kidnapper", "kamikaze", "three", "jumper", "ship", "mother", "octo"
self.type = "basic"
self.target = nil
self.bulletPools = ufoBullets
self.frame = 1
self.inScreen = false
self.warpTimer = 0
self.canWarp = false
self.used = false;
self.wasHit = false;
self.moveSpeed = 0
self.speedUpvol = 2.0
self.acc = 0.1
self.aniTime = 0
self.attackTime = 0
self.gasTime = 0
self.gas = 20000
self.noGas = nil
self.targetCheckGab = 0
self.targetCheckTime = 10
self.attackGap = 5000
self.scoreVol = 10
self.maxLife = 0
self.spawnGab = 5000
self.life = 0
self.dir_x = 0
self.dir_y = 0
self:setZIndex(1900)
self:checkImage(self.enemyImages)
local width, height = self:getSize()
local reWidth, reHeight = width * 0.7, height * 0.7
self:setCollideRect((width-reWidth)*0.5, (height-reHeight)*0.5, reWidth, reHeight)
self:setGroups(10)
--table.insert(enemy, self)
end
function Enemy:enable(x, y)
self.target = playerInstance
self.wasHit = false
self.targetAngle = 0
self.inScreen = false
self.canWarp = false
self.used = true;
self.aniTime = pd.getCurrentTimeMilliseconds()
self.gasTime = self.aniTime
self.attackTime = self.aniTime
self.noGas = false
self.targetCheckGab = self.aniTime
self.life = self.maxLife
self.dir_x = -vectorX
self.dir_y = -vectorY
self.angle_homing = 0
self.distance = 100
self:moveTo(x, y)
self:setSize(36, 36)
self:setVisible(true)
self:add()
end
--Do animation
function Enemy:doAnimation()
if self.life < 1 then
if timer(self.aniTime, 50) then
self.aniTime = pd.getCurrentTimeMilliseconds()
self.frame += 1
if self.frame >= 5 then
self:die(self.x, self.y)
end
self:checkImage(self.dieImages)
end
else
if timer(self.aniTime, 200) then
self.aniTime = pd.getCurrentTimeMilliseconds()
self.frame += 1
if self.frame > 2 then
self.frame = 1
end
self:checkImage(self.enemyImages)
end
end
end
--Change image by self.frame
function Enemy:checkImage(img)
local myImage = img
self:setImage(myImage[self.frame])
end
--Remove an enemy
function Enemy:disable()
self.used = false;
activeUFOCount -= 1
-- print("적기: "..activeUFOCount)
self:remove()
-- if self.type ~= "kidnapper" and self.type ~= "octo" then
-- enemySpawn(self.spawnGab, self.type)
-- end
--"remove")
end
--Process after enemy death
function Enemy:die(x, y)
self.used = false
enemyDieCheck()
-- makeFx(x, y, 5)
self:remove()
-- if self.type ~= "kidnapper" and self.type ~= "octo" then
-- enemySpawn(self.spawnGab, self.type)
-- end
end
--Get damage
function Enemy:getDamage(damage)
if self.life < 1 then
return
end
self.wasHit = true
local function hit()
self.wasHit = false
self:setVisible(true)
end
--print("hit")
self:setVisible(false)
SoundManager:playSound(1.0, 1, SoundManager.kSoundHit)
self.life -= damage
if self.life < 1 then
-- self:setSize(36, 36)
addScore(self.scoreVol)
if self.type == "mother" then
self:die(self.x, self.y)
else
self.frame = 1
self.aniTime = pd.getCurrentTimeMilliseconds()
makeAlien(self.x, self.y)
self:setVisible(true)
end
-- self:die(self.x, self.y)
else
pd.timer.performAfterDelay(10, hit)
end
end
function Enemy:targetCheck()
local currentX, currnetY = self:getPosition() --My position
local targetX, targetY = playerInstance:getPosition() --Target position
local distance_x = targetX - currentX
local distance_y = targetY - currnetY
local angle_self = math.atan(self.dir_y , self.dir_x);
local angle_target = math.atan(distance_y, distance_x);
local angle_abs = math.abs(angle_target - angle_self);
local angle_diff = 0;
if angle_abs < 6.28318548 - angle_abs then
angle_diff = angle_target - angle_self
else
if angle_target - angle_self > 0 then
angle_diff = -6.28318548 + (angle_target - angle_self)
else
angle_diff= 6.28318548 + (angle_target - angle_self)
end
end
self.angle_homing = angle_self + (angle_diff * self.acc)
if math.abs(self.angle_homing) > 3.14159274 then
if self.angle_homing > 0 then
self.angle_homing += -6.28318548
else
self.angle_homing += 6.28318548
end
end
end
--Attack
function Enemy:fire()
local bullet = ObjPools:getObj(self.bulletPools)
if bullet == nil then
return
end
local angle = getRadian(posX, posY, self.x, self.y)
local degree = angle * 180 / math.pi
local vX = math.cos(degree * math.pi/180)
local vY = math.sin(degree * math.pi/180)
bullet:enable(self.x, self.y, vX, vY, degree)
end
--Gas check
function Enemy:gasCheck()
if timer(self.gasTime, self.gas) then
self.noGas = true
end
end
--Attack check
function Enemy:attackCheck()
if self.attackGap == 0 or gameOver then
return
end
if timer(self.attackTime, self.attackGap) then
local gapX, _ = self:getSize()
self.attackTime = pd.getCurrentTimeMilliseconds()
if onScreen(self.x, self.y, gapX) then
self:fire()
end
end
end
--Event setting
function Enemy:event()
if self.type == "kidnapper" or self.type == "jumper" then
return
end
if self.noGas == false then
self:gasCheck()
if enemyAttackCheck() then
-- self:attackCheck()
end
end
end
--Moving
function Enemy:move()
if gameOver == false then
self:targetCheck()
end
self:warpCheck()
local moveSpeed = deltaTime * self.moveSpeed
if self.noGas then
self:moveBy(self.dir_x * moveSpeed * self.speedUpvol, self.dir_y * moveSpeed * self.speedUpvol)
else
self.dir_x = math.cos(self.angle_homing);
self.dir_y = math.sin(self.angle_homing);
self:moveBy(self.dir_x * moveSpeed, self.dir_y * moveSpeed )
-- self:moveWithCollisions(self.x + self.dir_x * moveSpeed, self.y + self.dir_y * moveSpeed )
end
end
--Hit check
function Enemy:hitCheck()
local distance = getDistance(posX, posY, self.x, self.y)
if distance <= 18 and gameOver == false then
if playerInstance:getInvicible() == false then
self:getDamage(2)
end
getDamage()
end
end
--Warp check
function Enemy:warpCheck()
local gapX, _ = self:getSize()
if onScreen(self.x, self.y, 200 + gapX) then
if self.canWarp == true then
self.canWarp = false
self.warpTimer = 0
-- print("dont warp")
end
else
if self.noGas or self.dropDown then
print("delete!!")
self:disable()
return
end
if self.canWarp == false then
self.canWarp = true
self.warpTimer = pd.getCurrentTimeMilliseconds()
else
if timer(self.warpTimer, 4000)then
-- print("warp!!")
enemyWarp(self)
end
end
end
end
-- Screen check
function Enemy:screenCheck()
local gapX, _ = self:getSize()
self.inScreen = onScreen(self.x, self.y, gapX)
if self.inScreen then
self:setVisible(true)
-- self:doAnimation()
-- print("In")
else
-- print("Out")
self:setVisible(false)
-- if self.type == "jumper" then
-- self:doAnimation()
-- end
end
end
function Enemy:update()
Enemy.super.update(self)
self:doAnimation()
self:event()
if self.life < 1 then
return
end
self:move()
self:hitCheck()
self:warpCheck()
self:screenCheck()
end
Have you looked at sampler?
This would give you a clearer idea of what time is going to which function.
Thank you for your tip.