SHOW:
|
|
- or go back to the newest paste.
1 | --[[ | |
2 | ||
3 | engine.lua | |
4 | - | Version: 1.1 |
4 | + | Version: 1.2 |
5 | ||
6 | Author: MegaLoler | |
7 | Email: megaloler9000@gmail.com | |
8 | ||
9 | A game engine for Love that manages game scenes and entities. | |
10 | ||
11 | If requires that you use the class.lua file: | |
12 | require("class.lua") | |
13 | ||
14 | Make sure you have this in your code: | |
15 | require("engine.lua") | |
16 | ||
17 | Create a game by creating a subclass of the Engine class. | |
18 | MyGame = engine.newClass(engine.Engine) | |
19 | ||
20 | Define the setup method: | |
21 | function MyGame:setup() | |
22 | -- Initalize your game here. | |
23 | end | |
24 | ||
25 | Make sure to call the setGame function of your game class in your main.lua: | |
26 | MyGame:setGame() -- This will make the game engine take over. | |
27 | ||
28 | Create a scene by subclassing the Entity class: | |
29 | TestScene = newClass(engine.Entity) | |
30 | ||
31 | Define the setup, update, draw, and any other methods that you wish: | |
32 | function TestScene:setup() ... end | |
33 | function TestScene:update(dt) ... end | |
34 | function TestScene:draw() ... end | |
35 | ||
36 | Call the setScene method somewhere in your game's setup method to set the inital scene on launch: | |
37 | self:setScene(TestScene) -- Pass the class itself, not an instance of it | |
38 | ||
39 | Add children entities to a scene or entity preferably in its setup method with the addChild method: | |
40 | local child = TestEntity:new(self.engine, self) -- Parameters are game engine and parent entity | |
41 | self:addChild(child) | |
42 | ||
43 | --]] | |
44 | ||
45 | require("class") | |
46 | ||
47 | engine = {} -- Engine package | |
48 | ||
49 | -- Love wrappers | |
50 | ||
51 | function love.load() | |
52 | engine.game:load() | |
53 | end | |
54 | ||
55 | function love.quit() | |
56 | engine.game:quit() | |
57 | end | |
58 | ||
59 | function love.focus(f) | |
60 | engine.game:focus(f) | |
61 | end | |
62 | ||
63 | function love.keypressed(key, unicode) | |
64 | engine.game:keypressed(key, unicode) | |
65 | end | |
66 | ||
67 | function love.keyreleased(key) | |
68 | engine.game:keyreleased(key) | |
69 | end | |
70 | ||
71 | function love.mousepressed(x, y, button) | |
72 | engine.game:mousepressed(x, y, button) | |
73 | end | |
74 | ||
75 | function love.mousereleased(x, y, button) | |
76 | engine.game:mousereleased(x, y, button) | |
77 | end | |
78 | ||
79 | function love.update(dt) | |
80 | engine.game:update(dt) | |
81 | end | |
82 | ||
83 | function love.draw() | |
84 | engine.game:draw() | |
85 | end | |
86 | ||
87 | -- Engine class | |
88 | ||
89 | engine.Engine = newClass() | |
90 | ||
91 | function engine.Engine:init() | |
92 | self:setSize() | |
93 | self:setTitle() | |
94 | self.focused = true | |
95 | self.fading = false | |
96 | self.fade = 1 | |
97 | self.fadeSpeed = 0.5 | |
98 | self.fadeColor = {255, 255, 255} | |
99 | self:setScene(engine.Entity) | |
100 | end | |
101 | ||
102 | -- Sets the engine subclass to load | |
103 | ||
104 | function engine.Engine:setGame() | |
105 | engine.game = self:new() | |
106 | end | |
107 | ||
108 | -- Engine methods | |
109 | ||
110 | function engine.Engine:setTitle(title) | |
111 | self.title = title or "Game Engine" | |
112 | love.graphics.setCaption(self.title) | |
113 | end | |
114 | ||
115 | function engine.Engine:setSize(width, height) | |
116 | self.width = width or 640 | |
117 | self.height = height or 480 | |
118 | love.graphics.setMode(self.width, self.height) | |
119 | if self.scene then self:positionScene() end | |
120 | end | |
121 | ||
122 | function engine.Engine:fadeToScene(scene, ...) | |
123 | self.fadeTo = scene | |
124 | self.fadeArgs = ... | |
125 | self.fading = true | |
126 | end | |
127 | ||
128 | function engine.Engine:setScene(scene, ...) | |
129 | if self.scene then self.scene:destroy() end | |
130 | self.scene = scene:new(self) | |
131 | self:positionScene() | |
132 | self.scene:setup(...) | |
133 | end | |
134 | ||
135 | function engine.Engine:toggleFullscreen() | |
136 | love.graphics.toggleFullscreen() | |
137 | end | |
138 | ||
139 | -- Wrapped Love functions | |
140 | ||
141 | function engine.Engine:load() | |
142 | self:setup() | |
143 | end | |
144 | ||
145 | function engine.Engine:quit() | |
146 | end | |
147 | ||
148 | function engine.Engine:focus(f) | |
149 | self.focused = f | |
150 | end | |
151 | ||
152 | function engine.Engine:keypressed(key, unicode) | |
153 | self.scene:onkeypressed(key, unicode) | |
154 | end | |
155 | ||
156 | function engine.Engine:keyreleased(key) | |
157 | self.scene:onkeyreleased(key) | |
158 | end | |
159 | ||
160 | function engine.Engine:mousepressed(x, y, button) | |
161 | self.scene:onmousepressed(x, y, button) | |
162 | end | |
163 | ||
164 | function engine.Engine:mousereleased(x, y, button) | |
165 | self.scene:onmousereleased(x, y, button) | |
166 | end | |
167 | ||
168 | function engine.Engine:update(dt) | |
169 | if self.fading then | |
170 | if self.fade >= 1 then | |
171 | self.fade = 1 | |
172 | self.fading = false | |
173 | self:setScene(self.fadeTo, self.fadeArgs) | |
174 | else | |
175 | self.fade = self.fade + self.fadeSpeed * dt | |
176 | if self.fade > 1 then self.fade = 1 end | |
177 | end | |
178 | else | |
179 | if self.fade <= 0 then | |
180 | self.fade = 0 | |
181 | else | |
182 | self.fade = self.fade - self.fadeSpeed * dt | |
183 | if self.fade < 0 then self.fade = 0 end | |
184 | end | |
185 | end | |
186 | self.scene:masterUpdate(dt) | |
187 | end | |
188 | ||
189 | function engine.Engine:draw() | |
190 | self.scene:render() | |
191 | love.graphics.setCanvas() | |
192 | self.scene:postRender() | |
193 | self:drawFade() | |
194 | end | |
195 | ||
196 | -- Private methods | |
197 | ||
198 | function engine.Engine:positionScene() | |
199 | self.scene:setPosition(self.width / 2, self.height / 2) | |
200 | end | |
201 | ||
202 | function engine.Engine:drawFade() | |
203 | love.graphics.setBlendMode("alpha") | |
204 | love.graphics.setColor(self.fadeColor[1], self.fadeColor[2], self.fadeColor[3], (math.cos((1 - self.fade) * math.pi) + 1) * 127) | |
205 | love.graphics.polygon("fill", 0, 0, self.width, 0, self.width, self.height, 0, self.height) | |
206 | end | |
207 | ||
208 | -- Virtual methods | |
209 | ||
210 | function engine.Engine:setup() | |
211 | end | |
212 | ||
213 | - | function engine.Entity:init(engine) |
213 | + | |
214 | ||
215 | engine.Entity = newClass() | |
216 | ||
217 | function engine.Entity:init(engine, container) | |
218 | self.engine = engine | |
219 | self.container = container | |
220 | self:setSize() | |
221 | self:setPosition() | |
222 | self:setOrientation() | |
223 | self:setScale() | |
224 | self:setColor() | |
225 | self.backgroundAlpha = 255 | |
226 | self.alpha = 255 | |
227 | self:setBackgroundColor() | |
228 | self.children = {} | |
229 | self.selectable = false | |
230 | self.selected = false | |
231 | self.clearCanvas = true | |
232 | self.hasBackground = false | |
233 | self.staticDrawing = true | |
234 | self.refreshRequested = true | |
235 | end | |
236 | ||
237 | -- Entity methods | |
238 | ||
239 | function engine.Entity:addChild(child, ...) | |
240 | - | self.x = x or 0 |
240 | + | |
241 | - | self.y = y or 0 |
241 | + | |
242 | end | |
243 | ||
244 | function engine.Entity:removeChild(child) | |
245 | table.remove(self.children, child) | |
246 | child:destroy() | |
247 | end | |
248 | ||
249 | function engine.Entity:refresh() | |
250 | self.refreshRequested = true | |
251 | end | |
252 | ||
253 | function engine.Entity:setColor(color) | |
254 | self.color = color or {255, 255, 255} | |
255 | end | |
256 | ||
257 | function engine.Entity:setBackgroundColor(color) | |
258 | local r, g, b = love.graphics.getBackgroundColor() | |
259 | self.backgroundColor = color or {r, g, b} | |
260 | end | |
261 | ||
262 | function engine.Entity:setPosition(x, y) | |
263 | local dx = self.engine.width / 2 | |
264 | local dy = self.engine.height / 2 | |
265 | if self.container then | |
266 | dx = self.container.width / 2 | |
267 | - | love.graphics.setCanvas(self.canvas) |
267 | + | dy = self.container.height / 2 |
268 | - | love.graphics.setColor(love.graphics.getBackgroundColor()) |
268 | + | |
269 | self.x = x or dx | |
270 | self.y = y or dy | |
271 | end | |
272 | - | self:preDraw() |
272 | + | |
273 | function engine.Entity:move(x, y) | |
274 | self.x = self.x + x | |
275 | - | v:render() |
275 | + | self.y = self.y + y |
276 | end | |
277 | - | v:postRender() |
277 | + | |
278 | function engine.Entity:setOrientation(r) | |
279 | self.orientation = r or 0 | |
280 | - | self:draw() |
280 | + | |
281 | ||
282 | function engine.Entity:setScale(x, y) | |
283 | self.scaleX = x or 1 | |
284 | self.scaleY = y or 1 | |
285 | - | love.graphics.setColor(self.color[1], self.color[2], self.color[3]) |
285 | + | |
286 | ||
287 | function engine.Entity:setSize(width, height) | |
288 | self.width = width or self.engine.width | |
289 | self.height = height or self.engine.height | |
290 | self.canvas = love.graphics.newCanvas(self.width, self.height) | |
291 | end | |
292 | ||
293 | function engine.Entity:masterUpdate(dt) | |
294 | for k, v in pairs(self.children) do | |
295 | v:masterUpdate(dt) | |
296 | end | |
297 | self:update(dt) | |
298 | end | |
299 | ||
300 | function engine.Entity:render() | |
301 | if not self.staticDrawing or self.refreshRequested then | |
302 | self.refreshRequested = false | |
303 | love.graphics.setCanvas(self.canvas) | |
304 | if self.clearCanvas then self.canvas:clear() end | |
305 | if self.hasBackground then | |
306 | love.graphics.setColor(self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3], self.backgroundAlpha) | |
307 | love.graphics.setBlendMode("alpha") | |
308 | love.graphics.polygon("fill", 0, 0, self.width, 0, self.width, self.height, 0, self.height) | |
309 | end | |
310 | ||
311 | self:preDraw() | |
312 | ||
313 | for k, v in pairs(self.children) do | |
314 | v:render() | |
315 | love.graphics.setCanvas(self.canvas) | |
316 | v:postRender() | |
317 | end | |
318 | ||
319 | self:draw() | |
320 | end | |
321 | end | |
322 | ||
323 | function engine.Entity:postRender() | |
324 | love.graphics.setBlendMode("alpha") | |
325 | love.graphics.setColor(self.color[1], self.color[2], self.color[3], self.alpha) | |
326 | love.graphics.drawq(self.canvas, love.graphics.newQuad(0, 0, self.width, self.height, self.width, self.height), self.x, self.y , self.orientation, self.scaleX, self.scaleY, self.width / 2, self.height / 2) | |
327 | end | |
328 | ||
329 | function engine.Entity:onkeypressed(key, unicode) | |
330 | self:keypressed(key, unicode) | |
331 | if self.selectedChild then self.selectedChild:onkeypressed(key, unicode) end | |
332 | end | |
333 | ||
334 | function engine.Entity:onkeyreleased(key) | |
335 | self:keyreleased(key) | |
336 | if self.selectedChild then self.selectedChild:onkeyreleased(key) end | |
337 | end | |
338 | ||
339 | function engine.Entity:onmousepressed(x, y, button) | |
340 | local oldSelectedChild = self.selectedChild | |
341 | self:mousepressed(x, y, button) | |
342 | if self.selectedChild then self.selectedChild.selected = false end | |
343 | self.selectedChild = self:getChildAtLocation(x, y) | |
344 | if self.selectedChild and not self.selectedChild.selectable then self.selectedChild = nil end | |
345 | if self.selectedChild then | |
346 | self.selectedChild.selected = true | |
347 | x, y = self:localCoordsToChildCoords(x, y, self.selectedChild) | |
348 | self.selectedChild:onmousepressed(x, y, button) | |
349 | end | |
350 | if self.selectedChild ~= oldSelectedChild then | |
351 | if oldSelectedChild then oldSelectedChild:focus(false) end | |
352 | if self.selectedChild then self.selectedChild:focus(true) end | |
353 | end | |
354 | end | |
355 | ||
356 | function engine.Entity:onmousereleased(x, y, button) | |
357 | self:mousereleased(x, y, button) | |
358 | if self.selectedChild then | |
359 | x, y = self:localCoordsToChildCoords(x, y, self.selectedChild) | |
360 | self.selectedChild:onmousereleased(x, y, button) | |
361 | if self.selectedChild:inBounds(x, y) then self.selectedChild:mouseclicked(x, y, button) end | |
362 | end | |
363 | end | |
364 | ||
365 | -- Private methods | |
366 | ||
367 | function engine.Entity:inBounds(x, y) | |
368 | return x >= 0 and x < self.width and y >= 0 and y < self.height | |
369 | end | |
370 | ||
371 | function engine.Entity:getChildAtLocation(x, y) | |
372 | for i = 1, #self.children do | |
373 | local child = self.children[#self.children - (i - 1)] | |
374 | local cx, cy = self:localCoordsToChildCoords(x, y, child) | |
375 | if child:inBounds(cx, cy) then return child end | |
376 | end | |
377 | end | |
378 | ||
379 | function engine.Entity:localCoordsToChildCoords(x, y, child) | |
380 | x = (x - child.x) / child.scaleX | |
381 | y = (y - child.y) / child.scaleY | |
382 | ||
383 | distance = math.sqrt((x * x) + (y * y)) | |
384 | angle = math.atan2(y, x) - child.orientation | |
385 | ||
386 | x = math.cos(angle) * distance | |
387 | y = math.sin(angle) * distance | |
388 | ||
389 | x = x + child.width / 2 | |
390 | y = y + child.height / 2 | |
391 | ||
392 | return x, y | |
393 | end | |
394 | ||
395 | -- Virtual methods | |
396 | ||
397 | function engine.Entity:setup(...) | |
398 | end | |
399 | ||
400 | function engine.Entity:destroy() | |
401 | end | |
402 | ||
403 | function engine.Entity:focus(f) | |
404 | end | |
405 | ||
406 | function engine.Entity:keypressed(key, unicode) | |
407 | end | |
408 | ||
409 | function engine.Entity:keyreleased(key) | |
410 | end | |
411 | ||
412 | function engine.Entity:mousepressed(x, y, button) | |
413 | end | |
414 | ||
415 | function engine.Entity:mousereleased(x, y, button) | |
416 | end | |
417 | ||
418 | function engine.Entity:mouseclicked(x, y, button) | |
419 | end | |
420 | ||
421 | function engine.Entity:update(dt) | |
422 | end | |
423 | ||
424 | function engine.Entity:preDraw() | |
425 | end | |
426 | ||
427 | function engine.Entity:draw() | |
428 | end |