2D Space Shooter Part 5: Firing

From GiderosMobile
Revision as of 13:38, 3 January 2020 by Hgy29 (talk | contribs) (→‎Testing)

Last chapter was quick, this one will be longer. We will now deal with weapons!

Cannons

Remember how we defined cannons in our Ship class ? It is now time to implement them. A cannon is basically something that will throw bullets repeatedly. Create a new file named 'cannon.lua' and paste the following code:

Cannon=Core.class(Object)

function Cannon:init(def,scale,ship)
	local shipx,shipy=ship:getAnchorPosition()
	self.x=def.x*scale-shipx
	self.y=def.y*scale-shipy
	self.type=def.type
	self.rate=def.rate
	self.angle=def.angle or 0
	self.ship=ship
	self.reload=0
end

function Cannon:fire()
	if self.reload==0 then
		local x,y=self.ship:getPosition()
		x+=self.x
		y+=self.y
		local bullet=Bullet.new(self.type,self.angle+self.ship:getRotation(),x,y)
		bullet.friendly=self.ship.friendly
		self.reload=self.rate
	else
		self.reload-=1
	end
end

The Cannon class isn't a Sprite, but a basic Object. In the 'init' method, we mostly copy the cannon definition locally and initialize our reload counter that will be used to count time between each shot. We also compute the ship-relative position of the cannon.

In the 'fire' method, we decrement the reload counter until it reaches 0. When at 0, we create a Bullet and reset our reload counter to the cannon rate value.

Bullets

The Bullet class needs more work: it will be a graphic object (inherited from Pixel), but will also have to check collisions. Here is the Bullet code, to copy into a bullet.lua file:

local BULLETS_DEF={
laser={ file="laser.png", speed=5, damage=1 },
missile={ file="rocket.png", speed=1, damage=10 },
}

Bullet=Core.class(Pixel,function (type) end)

function Bullet:init(type,angle,x,y)
	local bullet_def=BULLETS_DEF[type]
	assert(bullet_def,"No such bullet type: "..type)
	local texture=Texture.new("gfx/"..bullet_def.file,true)
	local tw,th=texture:getWidth(),texture:getHeight()
	local scale=0.3
	self:setTexture(texture)
	self:setDimensions(tw*scale,th*scale)
	self.damage=bullet_def.damage
	self.speed=bullet_def.speed
	self:setAnchorPoint(0.5,0.5)
	self:setRotation(angle)
	self:setPosition(x,y)
	local ax,ay=self:getAnchorPosition()
	self.dx,self.dy=math.sin(^<angle),math.cos(^<angle)
	BULLETS:addChild(self)
	-- Add to actors list
	ACTORS[self]=true
	-- Add to collision world
	BUMP_WORLD:add(self,x-ax,y-ay,tw*scale,th*scale)
end

function Bullet:destroy()
	-- Remove from collision world
	BUMP_WORLD:remove(self)
	-- Remove from actors list
	ACTORS[self]=nil
	-- Remove from screen
	self:removeFromParent()
end

-- This function will check collisions between this bullet (item) and some other object (other)
local function collision_filter(item,other)
	-- Other can be a Ship or another Bullet
	-- If it has a property named 'damage' then it is a Bullet, and we don't want bullets to collide with each others
	if other.damage then return nil end
	-- We also want to avoid being killed by our own shots, or enemies killing each others
	if item.friendly==other.friendly then return nil end
	-- Now we have a Bullet colliding with an enemy ship
	return 'touch'
end

function Bullet:tick(delay)
	local x,y=self:getPosition()
	x+=self.dx*self.speed
	y-=self.dy*self.speed
	self:setPosition(x,y)
	local cols,colslen
	x,y,cols,colslen=BUMP_WORLD:move(self,x,y,collision_filter)
	for k,col in pairs(cols) do
		col.other:hit(self.damage)
	end
	if x<SCR_LEFT or x>SCR_RIGHT or y<SCR_TOP or y>SCR_BOTTOM or colslen>0 then
		self:destroy()
	end
end

TODO: Write details

Testing

Now that our cannons and bullets are defined, we can uncomment the Cannon instancing in ships.lua, 'init' method:

		self.cannons[k]=Cannon.new(cdef,scale,self)

We'll also need to place the weapons graphics in gfx folder.

Now your ship will fire when you hold the mouse button down:

2D Space Shooter Part 6: Enemies