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: [email protected] | |
| 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 |