Skip to content

L1lith/Lilis-Game-Engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lili's Game Engine

This game engine is very modular, simple, and highly adaptable. It's adaptability comes from the intentional support for developing plugins which can suit any use case. This game engine is also designed from the ground up to work entirely using functional programming instead of object oriented programming, which I believe is a much better design pattern.

This game engine takes inspiration from web frameworks like Astro.build which emphasize clean code and interoperability as a fundamental design principle, rather than an afterthought. Many coding projects incur massive technical debt under a "ship now, fix later" mindset, which inevitably leads to large amounts wasted time which could have been prevented with more forethought. I spent multiple years designing this game engine, even rebuilding it from scratch so that it will stand the tests of time and save developers time and headaches. I did this by inventing cleaner and more effective design patterns that serve as the foundation of your game, primarily two things serve as the bedrock of the game engine:

  1. Functional Programming
  2. Signals

Signals are a concept borrowed a web UI library called SolidJS, but I built my own implementation of them called Jabr which has been disentangled from the specifics of web UI libraries. Basically we can take one of the most powerful tools from cutting edge web development and apply it to game development. In practical terms it's like a variable that we can automatically listen for changes in it's value. They are extremely flexible, and I believe they can be used to sidestep the messiness that arises when using classes in Object Oriented Programming! Instead of classes we can use callbacks, and JavaScript's async feature makes coordinating events and tasks across time simple.

More On Signals

Here is some example of how Jabr can be used in game development:

import {Signal} from 'jabr' const [getPlayerPos, setPlayerPos, addPlayerPosListener, removePlayerPosListener] = new Signal({x: 0, y: 10}) const playerPositionListener = (newValue, oldValue) => { console.log('New Player Position: ' + newValue) console.log('Old Player Position: ' + oldValue) } addPlayerPosListener(playerPositionListener) setPlayerPos({x: 8, y: -99}) // Now our listener is called, logging our old and new player positions removePlayerPosListener(playerPositionListener) setPlayerPos({x: 99, y: -190}) // Nothing is logged because we removed our listener

By passing this player signal to our physics engine we no longer need to manually tell the physics engine when our player position changes, or create custom listeners to handle the physics engine updating the player position. There is now a single source of truth for this player position variable that allows us to gracefully handle updates without needing to rely on a tangled web of classes. My game engine expands on this concept, giving you an effective base for your game engine that is written in as few lines of code as possible so you can study it inside and out and customize it to your heart's content.

Diving into an example usage of the game engine

Enough technical breakdown of why I love this game engine, let's dive into an example of how it looks in practice. Below is some example code, notice how each component of the game engine is manually initialized. While that does increase the number of lines of code by a small amount, it leaves room for you to swap these plugins out with your own choice of plugins. Yes, even the default core behaviors of the game engine are themselves plugins with easily inspectable source code. Without further adieu:

import createP5Renderer from "./createP5Renderer"; import createGameCore from "./createGameCore"; import createGameLoop from "./createGameLoop"; import createEntity from "./createEntity"; import createEntityList from "./createEntityList"; import createRenderSettings from "./createRenderSettings"; import { details } from "sandhands"; export default async function runGame(container) { const entity = createEntity(); const entities = createEntityList([entity]); window.entities = entities; const renderSettings = createRenderSettings({ container, setup: (p) => { console.log(p); p.createCanvas(1000, 1000); p.background(200); }, }); const gameCore = createGameCore({ plugins: [createGameLoop(), createP5Renderer(entities, renderSettings)], }); gameCore.events.on("tick", () => { const entity = entities.get()[0]; if (entity) entity.x = (entity.x + 1) % 100; }); await gameCore.mount(); return gameCore.unmount; }

Here we setup our plugins, tell the p5.js renderer how to initialize the canvas, and create an on-screen entity that automatically moves left-to-right across the screen in about 30 lines of code. While a full functional game would take more than this, this example highlights all of the most basic functionality that you need to make a game with the engine! While that is a neat feat, I am confident that this simplicity scales to even flushed out games because I battle tested it in my first published game Drawlf!

About

Yet another game engine by me

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages