Chopper Command: Sprites with Löve

In the book Löve for Lua Game Programming I mentioned in my previous post the author shows examples of using external libraries with Löve to control various things, such as animations. The Löve website has a long list of these libraries and they certainly make life easier. But as I wanted to port most of the code to other frameworks, I decided to do things the hard way. So here’s how I created the sprites for the game in Lua.

So in the game I have a GameSprite class which is the base class for every sprite.

GameSprite.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
 
local GameSprite = {}
 
 
 
function GameSprite.decorate(sprite, gameModel)	
 
 
 
	sprite.super = GameSprite
 
	sprite.gameModel = gameModel
 
	sprite.squaredRadius = 0
 
	sprite.active = true
 
	sprite.visible = true
 
	sprite.x = 0
 
	sprite.y = 0
 
	sprite.scaleX = 1
 
	sprite.scaleY = 1
 
	sprite.rotation = 0
 
	sprite.width = 0
 
	sprite.height = 0
 
	sprite.alpha = 1
 
	sprite.halfWidth = 100
 
	sprite.halfHeight = 100
 
	sprite.skin = nil
 
	sprite.animationFrames = {}
 
	sprite.animationIndex = 1
 
	sprite.animationInterval = 4
 
	sprite.animationTimer = 0
 
 
 
	function sprite:reset(type) 
 
	end
 
 
 
	function sprite:animate(dt)
 
 
 
		if #self.animationFrames > 0 and self.active then
 
 
 
			self.animationTimer = self.animationTimer + 1;
 
 
 
			if 	self.animationTimer > self.animationInterval then
 
				self.animationTimer = 0
 
				self.animationIndex = self.animationIndex % #self.animationFrames 
 
				self.animationIndex = self.animationIndex + 1
 
				self.skin = self.animationFrames[self.animationIndex]
 
			end
 
 
 
		end
 
 
 
	end
 
 
 
	function sprite:draw() 
 
 
 
		if self.visible == false or self.alpha == 0 then
 
			return 
 
		end
 
 
 
		if self.alpha < 1 then
 
			love.graphics.setColor(255,255,255, self.alpha * 255)
 
		end
 
 
 
		love.graphics.draw(self.skin, self.x, self.y, self.rotation, self.scaleX, self.scaleY, self.width * 0.5, self.height * 0.5)
 
 
 
		if self.alpha < 1 then
 
			love.graphics.setColor(255,255,255,255)
 
		end
 
 
 
	end
 
 
 
end
 
 
 
return GameSprite

As I mentioned before, I’ll use the decorator pattern to create my game objects. And as you can see the class needs all the basic properties for sprites: x, y, width, height, scaleX, scaleY, rotation, alpha, visible.

If the sprite is animated, the frames will be stored inside a table and the update call will run the animate function which switches the frames. A very simple and rather crude implementation, but good enough for this old style arcade game

Now every sprite will extend this. As an example, the truck class.

truck.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
 
local Truck = {}
 
 
 
local GameSprite = require("gamesprite")
 
local constants = require ("constants")
 
 
 
 
 
function Truck.new (gameModel)	
 
 
 
	local sprite = {}
 
 
 
	GameSprite.decorate(sprite, gameModel)
 
 
 
	sprite._blinking = false
 
    sprite._blinkCnt = 0
 
    sprite._blinkTimer = 0
 
    sprite._blinkInterval = 2
 
    sprite.squaredRadius = 16 * 16
 
 
 
	local frame1 = love.graphics.newImage ( "sprites/truck1.png" )
 
	local frame2 = love.graphics.newImage ( "sprites/truck2.png" )
 
 
 
    sprite.animationFrames = { frame1, frame2}
 
    sprite.skin = sprite.animationFrames[1]
 
 
 
	sprite.width = frame1:getWidth()
 
	sprite.height = frame2:getHeight()
 
 
 
    sprite:reset ( constants.RESET_HARD )
 
 
 
	function sprite:reset (type)
 
 
 
		if type == constants.RESET_HARD then
 
 
 
			self._blinking = false
 
			self._blinkCnt = 0
 
			self._blinkTimer = 0
 
			self.visible = true
 
			self.active = true
 
			self.alpha = 1
 
 
 
		end
 
	end
 
 
 
 
 
	function sprite:update (dt)
 
 
 
		self:animate(dt)
 
 
 
		if visible == false then
 
			return 
 
		end 
 
 
 
		if self._blinking == true then
 
			if self._blinkCnt % 2 == 0 then	self.alpha = 0.1 else alpha = 1 end
 
			self._blinkCnt = self._blinkCnt + 1
 
			if self._blinkCnt == 10 then
 
				self.alpha = 1
 
				self.visible = false
 
			end
 
		end
 
 
 
	end
 
 
 
	function sprite:hit ()
 
 
 
		self._blinking = true
 
		self.alpha = 0.2
 
		self.active = false
 
 
 
	end
 
 
 
	return sprite
 
end
 
 
 
return Truck

Now if I want to add these to the main stage I can do it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 
-- declare main variables
 
local constants = require ("constants")
 
local Truck = require ("truck")
 
 
 
 
 
local canvas_bg
 
 
 
local gameModel = {running = true}
 
 
 
local truck1
 
local truck2
 
local truck3
 
 
 
-- load all assets
 
function love.load(arg)
 
 
 
	if arg[#arg] == "-debug" then require("mobdebug").start() end 
 
 
 
   	local bg = love.graphics.newImage ( "sprites/game_bg.png" )
 
 
 
	canvas_bg = love.graphics.newCanvas(constants.SCREEN_WIDTH, constants.SCREEN_HEIGHT)
 
	love.graphics.setCanvas(canvas_bg)
 
        love.graphics.draw( bg, 0, 0 )
 
    love.graphics.setCanvas()
 
 
 
	init()
 
end
 
 
 
 
 
function init ()
 
 
 
	truck1 = Truck.new(gameModel)
 
	truck2 = Truck.new(gameModel)
 
	truck3 = Truck.new(gameModel)
 
 
 
	truck1.y = 524
 
	truck2.y = 524
 
	truck3.y = 524
 
 
 
	truck1.x = 550
 
	truck2.x = 700
 
	truck3.x = 850
 
 
 
	truck3.alpha = .5
 
end
 
 
 
-- update game
 
function love.update (dt)
 
 
 
	truck1:update(dt)
 
	truck2:update(dt)
 
	truck3:update(dt)
 
 
 
end
 
 
 
 
 
-- draw everything
 
function love.draw()
 
 
 
	love.graphics.draw(canvas_bg)
 
	truck1:draw()
 
	truck2:draw()
 
	truck3:draw()
 
end

If you run the scene you will see the three trucks animating and one of them with alpha set to 50%

Next I’ll add the remaining elements used in the GameTile object