SHOW:
|
|
- or go back to the newest paste.
| 1 | --[[ | |
| 2 | ||
| 3 | engine.lua | |
| 4 | - | Version: 1.2 |
| 4 | + | Version: 1.3 |
| 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 |
| 96 | + | self.fade = 0 |
| 97 | - | self.fadeSpeed = 0.5 |
| 97 | + | self.fadeSpeed = 2 |
| 98 | - | self.fadeColor = {255, 255, 255}
|
| 98 | + | self:setFadeColor() |
| 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:setFadeColor(color) | |
| 129 | self.fadeColor = color or {0, 0, 0}
| |
| 130 | end | |
| 131 | ||
| 132 | function engine.Engine:setScene(scene, ...) | |
| 133 | if self.scene then self.scene:destroy() end | |
| 134 | self.scene = scene:new(self) | |
| 135 | self:positionScene() | |
| 136 | self.scene:setup(...) | |
| 137 | end | |
| 138 | ||
| 139 | function engine.Engine:toggleFullscreen() | |
| 140 | love.graphics.toggleFullscreen() | |
| 141 | end | |
| 142 | ||
| 143 | -- Wrapped Love functions | |
| 144 | ||
| 145 | function engine.Engine:load() | |
| 146 | self:setup() | |
| 147 | end | |
| 148 | ||
| 149 | function engine.Engine:quit() | |
| 150 | end | |
| 151 | ||
| 152 | function engine.Engine:focus(f) | |
| 153 | self.focused = f | |
| 154 | end | |
| 155 | ||
| 156 | function engine.Engine:keypressed(key, unicode) | |
| 157 | self.scene:onkeypressed(key, unicode) | |
| 158 | end | |
| 159 | ||
| 160 | function engine.Engine:keyreleased(key) | |
| 161 | self.scene:onkeyreleased(key) | |
| 162 | end | |
| 163 | ||
| 164 | function engine.Engine:mousepressed(x, y, button) | |
| 165 | self.scene:onmousepressed(x, y, button) | |
| 166 | end | |
| 167 | ||
| 168 | function engine.Engine:mousereleased(x, y, button) | |
| 169 | self.scene:onmousereleased(x, y, button) | |
| 170 | end | |
| 171 | ||
| 172 | function engine.Engine:update(dt) | |
| 173 | if self.fading then | |
| 174 | if self.fade >= 1 then | |
| 175 | self.fade = 1 | |
| 176 | self.fading = false | |
| 177 | self:setScene(self.fadeTo, self.fadeArgs) | |
| 178 | else | |
| 179 | self.fade = self.fade + self.fadeSpeed * dt | |
| 180 | if self.fade > 1 then self.fade = 1 end | |
| 181 | end | |
| 182 | else | |
| 183 | if self.fade <= 0 then | |
| 184 | self.fade = 0 | |
| 185 | else | |
| 186 | self.fade = self.fade - self.fadeSpeed * dt | |
| 187 | if self.fade < 0 then self.fade = 0 end | |
| 188 | end | |
| 189 | end | |
| 190 | self.scene:masterUpdate(dt) | |
| 191 | end | |
| 192 | ||
| 193 | function engine.Engine:draw() | |
| 194 | self.scene:render() | |
| 195 | love.graphics.setCanvas() | |
| 196 | self.scene:postRender() | |
| 197 | self:drawFade() | |
| 198 | end | |
| 199 | ||
| 200 | -- Private methods | |
| 201 | ||
| 202 | function engine.Engine:positionScene() | |
| 203 | self.scene:setPosition(self.width / 2, self.height / 2) | |
| 204 | end | |
| 205 | ||
| 206 | function engine.Engine:drawFade() | |
| 207 | love.graphics.setBlendMode("alpha")
| |
| 208 | love.graphics.setColor(self.fadeColor[1], self.fadeColor[2], self.fadeColor[3], (math.cos((1 - self.fade) * math.pi) + 1) * 127) | |
| 209 | love.graphics.polygon("fill", 0, 0, self.width, 0, self.width, self.height, 0, self.height)
| |
| 210 | end | |
| 211 | ||
| 212 | -- Virtual methods | |
| 213 | ||
| 214 | function engine.Engine:setup() | |
| 215 | end | |
| 216 | ||
| 217 | -- Entity class | |
| 218 | ||
| 219 | engine.Entity = newClass() | |
| 220 | ||
| 221 | function engine.Entity:init(engine, container) | |
| 222 | self.engine = engine | |
| 223 | self.container = container | |
| 224 | self:setPosition() | |
| 225 | self:setSize() | |
| 226 | self:setOrientation() | |
| 227 | self:setScale() | |
| 228 | self:setColor() | |
| 229 | self.backgroundAlpha = 255 | |
| 230 | self.alpha = 255 | |
| 231 | self:setBackgroundColor() | |
| 232 | self.children = {}
| |
| 233 | self.selectable = false | |
| 234 | self.selected = false | |
| 235 | self.clearCanvas = true | |
| 236 | self.hasBackground = false | |
| 237 | self.staticDrawing = true | |
| 238 | self.refreshRequested = true | |
| 239 | self.tick = 0 | |
| 240 | self.time = 0 | |
| 241 | end | |
| 242 | ||
| 243 | -- Entity methods | |
| 244 | ||
| 245 | function engine.Entity:addChild(child, ...) | |
| 246 | table.insert(self.children, child) | |
| 247 | child:setup(...) | |
| 248 | end | |
| 249 | ||
| 250 | function engine.Entity:removeChild(child) | |
| 251 | table.remove(self.children, child) | |
| 252 | child:destroy() | |
| 253 | end | |
| 254 | ||
| 255 | function engine.Entity:refresh() | |
| 256 | self.refreshRequested = true | |
| 257 | end | |
| 258 | ||
| 259 | function engine.Entity:setColor(color) | |
| 260 | self.color = color or {255, 255, 255}
| |
| 261 | end | |
| 262 | ||
| 263 | function engine.Entity:setBackgroundColor(color) | |
| 264 | local r, g, b = love.graphics.getBackgroundColor() | |
| 265 | self.backgroundColor = color or {r, g, b}
| |
| 266 | end | |
| 267 | ||
| 268 | function engine.Entity:setPosition(x, y) | |
| 269 | local dx = self.engine.width / 2 | |
| 270 | local dy = self.engine.height / 2 | |
| 271 | if self.container then | |
| 272 | dx = self.container.width / 2 | |
| 273 | dy = self.container.height / 2 | |
| 274 | end | |
| 275 | self.x = x or dx | |
| 276 | self.y = y or dy | |
| 277 | end | |
| 278 | ||
| 279 | function engine.Entity:move(x, y) | |
| 280 | self.x = self.x + x | |
| 281 | self.y = self.y + y | |
| 282 | end | |
| 283 | ||
| 284 | function engine.Entity:setOrigin(x, y) | |
| 285 | self.originX = x or self.width / 2 | |
| 286 | self.originY = y or self.height / 2 | |
| 287 | end | |
| 288 | ||
| 289 | function engine.Entity:setOrientation(r) | |
| 290 | self.orientation = r or 0 | |
| 291 | end | |
| 292 | ||
| 293 | function engine.Entity:setScale(x, y) | |
| 294 | self.scaleX = x or 1 | |
| 295 | self.scaleY = y or 1 | |
| 296 | end | |
| 297 | ||
| 298 | function engine.Entity:setSize(width, height) | |
| 299 | self.width = width or self.engine.width | |
| 300 | self.height = height or self.engine.height | |
| 301 | self.canvas = love.graphics.newCanvas(self.width, self.height) | |
| 302 | self:setOrigin() | |
| 303 | end | |
| 304 | ||
| 305 | function engine.Entity:masterUpdate(dt) | |
| 306 | self.time = self.time + dt | |
| 307 | for k, v in pairs(self.children) do | |
| 308 | v:masterUpdate(dt) | |
| 309 | end | |
| 310 | self:update(dt) | |
| 311 | self.tick = self.tick + 1 | |
| 312 | end | |
| 313 | ||
| 314 | function engine.Entity:render() | |
| 315 | if not self.staticDrawing or self.refreshRequested then | |
| 316 | self.refreshRequested = false | |
| 317 | love.graphics.setCanvas(self.canvas) | |
| 318 | if self.clearCanvas then self.canvas:clear() end | |
| 319 | if self.hasBackground then | |
| 320 | love.graphics.setColor(self.backgroundColor[1], self.backgroundColor[2], self.backgroundColor[3], self.backgroundAlpha) | |
| 321 | love.graphics.setBlendMode("alpha")
| |
| 322 | love.graphics.polygon("fill", 0, 0, self.width, 0, self.width, self.height, 0, self.height)
| |
| 323 | end | |
| 324 | ||
| 325 | self:preDraw() | |
| 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) |
| 326 | + | |
| 327 | for k, v in pairs(self.children) do | |
| 328 | v:render() | |
| 329 | love.graphics.setCanvas(self.canvas) | |
| 330 | v:postRender() | |
| 331 | end | |
| 332 | ||
| 333 | self:draw() | |
| 334 | end | |
| 335 | end | |
| 336 | ||
| 337 | function engine.Entity:postRender() | |
| 338 | love.graphics.setBlendMode("alpha")
| |
| 339 | love.graphics.setColor(self.color[1], self.color[2], self.color[3], self.alpha) | |
| 340 | 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.originX, self.originY) | |
| 341 | end | |
| 342 | ||
| 343 | function engine.Entity:onkeypressed(key, unicode) | |
| 344 | self:keypressed(key, unicode) | |
| 345 | if self.selectedChild then self.selectedChild:onkeypressed(key, unicode) end | |
| 346 | end | |
| 347 | ||
| 348 | function engine.Entity:onkeyreleased(key) | |
| 349 | self:keyreleased(key) | |
| 350 | if self.selectedChild then self.selectedChild:onkeyreleased(key) end | |
| 351 | end | |
| 352 | ||
| 353 | function engine.Entity:onmousepressed(x, y, button) | |
| 354 | local oldSelectedChild = self.selectedChild | |
| 355 | self:mousepressed(x, y, button) | |
| 356 | if self.selectedChild then self.selectedChild.selected = false end | |
| 357 | self.selectedChild = self:getChildAtLocation(x, y) | |
| 358 | if self.selectedChild and not self.selectedChild.selectable then self.selectedChild = nil end | |
| 359 | if self.selectedChild then | |
| 360 | self.selectedChild.selected = true | |
| 361 | x, y = self:localCoordsToChildCoords(x, y, self.selectedChild) | |
| 362 | self.selectedChild:onmousepressed(x, y, button) | |
| 363 | end | |
| 364 | if self.selectedChild ~= oldSelectedChild then | |
| 365 | if oldSelectedChild then oldSelectedChild:focus(false) end | |
| 366 | if self.selectedChild then self.selectedChild:focus(true) end | |
| 367 | end | |
| 368 | end | |
| 369 | ||
| 370 | function engine.Entity:onmousereleased(x, y, button) | |
| 371 | self:mousereleased(x, y, button) | |
| 372 | if self.selectedChild then | |
| 373 | x, y = self:localCoordsToChildCoords(x, y, self.selectedChild) | |
| 374 | self.selectedChild:onmousereleased(x, y, button) | |
| 375 | if self.selectedChild:inBounds(x, y) then self.selectedChild:mouseclicked(x, y, button) end | |
| 376 | end | |
| 377 | end | |
| 378 | ||
| 379 | -- Private methods | |
| 380 | ||
| 381 | function engine.Entity:inBounds(x, y) | |
| 382 | return x >= 0 and x < self.width and y >= 0 and y < self.height | |
| 383 | end | |
| 384 | ||
| 385 | function engine.Entity:getChildAtLocation(x, y) | |
| 386 | for i = 1, #self.children do | |
| 387 | local child = self.children[#self.children - (i - 1)] | |
| 388 | local cx, cy = self:localCoordsToChildCoords(x, y, child) | |
| 389 | - | x = x + child.width / 2 |
| 389 | + | |
| 390 | - | y = y + child.height / 2 |
| 390 | + | |
| 391 | end | |
| 392 | ||
| 393 | function engine.Entity:localCoordsToChildCoords(x, y, child) | |
| 394 | x = (x - child.x) / child.scaleX | |
| 395 | y = (y - child.y) / child.scaleY | |
| 396 | ||
| 397 | distance = math.sqrt((x * x) + (y * y)) | |
| 398 | angle = math.atan2(y, x) - child.orientation | |
| 399 | ||
| 400 | x = math.cos(angle) * distance | |
| 401 | y = math.sin(angle) * distance | |
| 402 | ||
| 403 | x = x + child.originX | |
| 404 | y = y + child.originY | |
| 405 | ||
| 406 | return x, y | |
| 407 | end | |
| 408 | ||
| 409 | -- Virtual methods | |
| 410 | ||
| 411 | function engine.Entity:setup(...) | |
| 412 | end | |
| 413 | ||
| 414 | function engine.Entity:destroy() | |
| 415 | end | |
| 416 | ||
| 417 | function engine.Entity:focus(f) | |
| 418 | end | |
| 419 | ||
| 420 | function engine.Entity:keypressed(key, unicode) | |
| 421 | end | |
| 422 | ||
| 423 | function engine.Entity:keyreleased(key) | |
| 424 | end | |
| 425 | ||
| 426 | function engine.Entity:mousepressed(x, y, button) | |
| 427 | end | |
| 428 | ||
| 429 | function engine.Entity:mousereleased(x, y, button) | |
| 430 | end | |
| 431 | ||
| 432 | function engine.Entity:mouseclicked(x, y, button) | |
| 433 | end | |
| 434 | ||
| 435 | function engine.Entity:update(dt) | |
| 436 | end | |
| 437 | ||
| 438 | function engine.Entity:preDraw() | |
| 439 | end | |
| 440 | ||
| 441 | function engine.Entity:draw() | |
| 442 | end |