{"id":20619029,"url":"https://github.com/tessapower/tengine","last_synced_at":"2026-04-24T21:33:26.640Z","repository":{"id":188839766,"uuid":"476063836","full_name":"tessapower/TEngine","owner":"tessapower","description":"TEngine: my very first 2D game engine","archived":false,"fork":false,"pushed_at":"2022-12-17T09:26:37.000Z","size":6528,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-05T10:07:09.269Z","etag":null,"topics":["design-patterns","design-patterns-java","game-engine","game-engine-2d","java-17","maven"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tessapower.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-03-30T21:56:53.000Z","updated_at":"2024-10-07T10:25:36.000Z","dependencies_parsed_at":"2023-08-17T05:12:04.733Z","dependency_job_id":null,"html_url":"https://github.com/tessapower/TEngine","commit_stats":null,"previous_names":["tessapower/tengine"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tessapower/TEngine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessapower%2FTEngine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessapower%2FTEngine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessapower%2FTEngine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessapower%2FTEngine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tessapower","download_url":"https://codeload.github.com/tessapower/TEngine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tessapower%2FTEngine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32241794,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T13:21:15.438Z","status":"ssl_error","status_checked_at":"2026-04-24T13:21:15.005Z","response_time":64,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["design-patterns","design-patterns-java","game-engine","game-engine-2d","java-17","maven"],"created_at":"2024-11-16T12:10:15.595Z","updated_at":"2026-04-24T21:33:26.634Z","avatar_url":"https://github.com/tessapower.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TEngine\n\n[![Maven Build](https://github.com/tessapower/TEngine/actions/workflows/maven.yml/badge.svg)](https://github.com/tessapower/TEngine/actions/workflows/maven.yml)\n\nA 2D game engine built in Java based on ECS architecture. See it in action [here!](https://github.com/tessapower/snek)\n\n`TEngine` (pronounced _\"ten-gin\"_, short for _\"Tessa's Engine\"_) developed out of the game engine\nsupplied by the lecturers of the 159.261 Games Programming course at Massey University. I\nfleshed out the original lone little `GameEngine.java` into what you see here, and used it to\nbuild the games that I submitted for course assignments.\n\n`TEngine` was built using Java 17 and uses Java Swing for window management (based on requirement\nfor the course). The structure of game objects (called `Actor`s) is loosely based on the ECS\nmodel. There is support for actors and actor management, level and world management, audio,\ncomposite primitive containers, transforms, text, animated sprites and sprite sequences, and\nbasic broad-phase collision detection and collision event notifications.\n\n## Requirements\n\n- Minimum Java SE 17+\n- IntelliJ, Eclipse, or your favourite Java editor\n- Maven (install locally for command-line-only builds)\n\n## How to Build \u0026 Package `TEngine`\n\nThis project was developed using the Maven build system. You can either build and package the\nproject straight from the command line or using your favourite Java IDE that has support for Maven.\n\nWhen first opening the project, you'll see the project plugins and dependencies being downloaded.\nIt may take a minute for the project to process and index all the files. You can use the\npre-existing Maven goals to install the project dependencies and package up the `TEngine` into a\n`.jar` for use in other projects.\n\nTo build from the command-line only, you will need to have Maven installed locally. You can then\nuse Maven to install the necessary dependencies and package up the `TEngine` into a `.jar` for use\nin other projects. Enter the following commands in the project root:\n\n```shell\n# install dependencies\nmvn install\n# package the game engine\nmvn package\n```\n\nA file called `TEngine.jar` will be created in the `out/artifacts/TEngine_jar/` dir. This `.jar` can\nbe included in external projects in the usual way you link to external libraries.\n\n## Architecture\n\n`TEngine` is a 2D game engine that makes use of an ECS model to create and coordinate Actors. It\nhas a graphics engine, and physics engine,\n\n- Actors \u0026 Actor Management: Supported ✅\n- Audio: Supported ✅\n- Graphics Engine: Supported ✅\n    - Drawing primitives: circles, rectangles\n    - Compound primitive containers\n    - Text\n    - Sprites\n    - Animated Sprites \u0026 Sprite Sequences\n    - Transforms\n- Physics Engine: WIP 🛠️\n    - Broad-phase collision detection for AABBs ✅\n    - Broad-phase collision detection for Circles\n    - Collision detection events ✅\n    - Collision events notifications ✅\n    - Collision resolution\n- World Management: Supported ✅\n\n### Package Diagram\n\n![Package Diagram of TEngine](docs/package-diagram.svg)\n\n## Working with TEngine\n\nFor a practical example of working with `TEngine`, see the `snek!` game [here](https://github.com/tessapower/snek).\n\n- [Creating a Game](#creating-a-game)\n- [`World` and `Actor` Management](#world-and-actor-management)\n- [`Actor`s and the ECS Model](#actors-and-the-ecs-model)\n    - [The `Actor` Class](#the-actor-class)\n    - [Components of an `Actor`](#components-of-an-actor)\n    - [Creating an `Actor`](#creating-an-actor)\n- [`GraphicsEngine`](#graphicsengine)\n    - [`TGraphicObject`](#tgraphicobject)\n    - [Using `TGraphicCompound` to Compose a Graphic](#using-tgraphiccompound-to-compose-a-graphic)\n    - [`Sprite`](#sprite)\n    - [`AnimatedSprite`](#animatedsprite)\n- [`PhysicsEngine`](#physicsengine)\n\n---\n\n### Creating a Game\n\nTo create your own game, first create a new class that extends `GameEngine`, for now we'll refer\nto it as `Game`. The `GameEngine` will take care of creating a window, handling mouse and key\nevents, and running the core game loop for you. Your `Game` class will take care of the game logic,\nand being fun! :)\n\nInside `Game` is where you might decide to coordinate and manage various screens, e.g. a menu,\nhigh score, or game over screen, the state of the game, e.g. playing, paused, game over, etc,\nthe current level, (called a `World`), and anything else that relates to your game logic.\n\nThere are a variety of methods that you can override so your `Game` can set itself up, load levels,\nand listen for mouse, key, and collision events. As an example, the structure of your `Game`\nclass might look like this:\n\n\u003cimg alt=\"Example of Game Structure\" src=\"docs/game-example-2.svg\" width=\"600\" /\u003e\n\nYour `Game` class is the entrypoint for the program, so you will define a `main` like so:\n\n```java\npublic class Game extends GameEngine {\n    private static int FRAMERATE = 60;\n\n    public static void main(String[] args) {\n        createGame(new Game(), FRAMERATE);\n    }\n\n    public void init() { /* ... */ }\n\n    // ...\n}\n```\n\nAny custom setup for your game prior to starting can be done by overriding the `init()` method. To\nset a custom window dimension and title, call `setWindowProperties(Dimension, String)` from within\n`init()`.\n\n---\n\n### `World` and `Actor` Management\n\nConceptually, a `World` is the same as level in a game. You can create multiple different\n`World`s to represent different levels of your game. You can also maintain and manipulate the same\n`World` throughout the game by adding and destroying `Actor`s (explained in the next section) and\nmake changes while the player is playing.\n\n\u003cimg alt=\"World \u0026 Actor Management\" src=\"docs/actor-world-management-2.svg\" width=\"400\" /\u003e\n\nAt the beginning of your game, you need to load a `World` into the `GameEngine` by calling\n`loadWorld(World)` on your `Game` class. To swap out one `World` for another, first call\n`unloadWorld(World)` and then `loadWorld(World)`.\n\n---\n\n### `Actor`s and the ECS model\n\nAn _Actor_, a.k.a. _Game Object_, _Game Entity_, or _Node_, is a general purpose object that\ninteracts with your game, and responds to player input or other Actors in the world. An `Actor`\nin `TEngine` is modelled on an _Entity Component System_ (ECS).\n\nIn an ECS, an Actor has many individual components to represent its data, characteristics, or\nproperties. A game engine using an ECS has _systems_ which operate on these components, e.g.\nphysics, graphics, AI, collisions, etc. The Actor is usually only used to loosely associate the\ncomponents to one another via pointers, references, or even simply using an ID.\n\n#### The `Actor` Class\n\n`Actor` is the base class for an object that can be added to a `World`. Anything that the player\ncan control or interact with in the `World` is considered an `Actor`. This base class has an\ninitial set of methods and data that you may find useful. There are a few that you should pay\nparticular attention to:\n\n- `velocity`, `setVelocity()` and `velocity()`: these do what you expect, **without** requiring\n  your `Actor` to have a `TPhysicsComponent` to work. This is because you usually want to be able\n  to update the position (`origin`) of your `Actor` and all of its components using `setOrigin\n  (TPoint)`. By default, `setOrigin(TPoint)` will propagate this change to its graphical and\n  physical components, but you can override this method to customize this behavior.\n\n- `setWorld(World)`: Associates this `Actor` with a particular `World`. This can be used to pass\n  your `Actor` between `World`s.\n\n\u003e **Warning**\n\u003e\n\u003e **You do not need to call this method directly**. This method is called by `World` when you\n\u003e add an `Actor` to it by calling `myWorld.add(myActor)`.\n\n- `destroyWhenOffScreen(bool)`: set whether this `Actor` should be destroyed when it goes offscreen.\n  You can query this setting by calling `destroyWhenOffScreen()`.\n\n- `markPendingDestroy()`: destroy the `Actor` on the next update.\n\n- `destroy()`: removes the graphic from the `World`s graphical canvas, and the `Actor` from the \n  world's list of `Actor`s.\n\n\u003e **Warning**\n\u003e\n\u003e You only need to mark an `Actor` to be destroyed, and `destroy()` is called automatically by\n\u003e `GameEngine` in the update loop.\n\n#### Components of an `Actor`\n\nAn `Actor` comprises two components: a graphical representation (`TGraphicObject`), and an\noptional physical representation (`TPhysicsComponent`). By default, an `Actor` does not have a\ngraphical or physical representation. These components must be created and associated with the\n`Actor` in the extending class constructor before the `Actor` can be used.\n\n\u003e **Note**\n\u003e\n\u003e You can choose not to give your `Actor` a physical component if it doesn't make sense for your\n\u003e game, but it **must** have a graphical component.\n\n#### Creating an `Actor`\n\nBelow is a skeleton class for an `Actor` that represents the player. You may want to keep\nsmall pieces of data in here, such as the number of lives or the player's score. Alternatively\nencapsulate this data in a separate `PlayerModel` component and associate that object with this\nclass.\n\n```java\nimport tengine.graphics.*;\n\npublic class Player extends Actor {\n    private final PlayerModel model = new PlayerModel();\n    private final Dimension dimension;\n\n    private Player(Dimension dimension) {\n        // Initialize any private data members here...\n        this.dimension = dimension;\n\n        // Initialize graphic\n        graphic = initSprite(dimension);\n\n        // Initialize physics (optional)\n        // physicsBody = initPhysics(dimension);\n\n        // ...\n    }\n\n    /**\n     * Initialize the graphic for this Player. You can use any Graphics class\n     * that extends from TGraphicObject, e.g. TRect, AnimatedSprite, or\n     * even a TGraphicCompound.\n     */\n    private TSprite initSprite(Dimension dim) {\n        // Initialize the Player's graphic here! You can also choose to \n        // do this step in the constructor if this setup is trivial.\n        // ...\n    }\n\n    /**\n     * Call this method from within your World's update method to update\n     * this Player every tick. You don't need to worry about updating the\n     * graphic or physicsBody from here, that's handled by the GraphicsEngine\n     * and PhysicsEngine!\n     */\n    public void update() {\n        // Update the internal state of the Player here. This could be\n        // moving to the next GridSquare, checking for game over conditions,\n        // updating the Player's score, etc.\n        model.update();\n    }\n\n    public boolean hasDied() { return model.numLives() == 0; }\n\n    public boolean handleKeyEvent(KeyEvent keyEvent) { /* ... */ }\n    // etc...\n}\n```\n\n#### Adding an `Actor` to the `World`\n\nAdding `Actor`s to the `World` can be done by using the public methods on the base `World` class:\n\n- `add(Actor)`: adds a single `Actor` to a `World`, and\n- `add(Actor...)`: adds a variable number of `Actor`s to a `World`, e.g. `myWorld.add(player,\n  opponent, ball, playerGoal, opponentGoal)`\n\n\u003e **Warning**\n\u003e\n\u003e You **do not** need to call the `setWorld(World)` method on an `Actor`, this is taken care of\n\u003e by the `World` itself:\n\u003e\n\u003e \u003cimg alt=\"Add Actor to World\" src=\"docs/add-actor-to-world.svg\" width=\"400\" /\u003e\n\n#### Destroying an `Actor`\n\nRemoving `Actor`s from the `World` can be done by marking the actor to be destroyed with the public\nmethod on the base `Actor` class `markPendingDestroy()`. From there, the `GameEngine` will take\ncare of removing destroying the `Actor`.\n\n\u003e **Warning**\n\u003e\n\u003e You **do not** need to call the `remove(Actor)` method on a `World`, this is taken care of by\n\u003e the `Actor` itself:\n\u003e\n\u003e \u003cimg alt=\"Destroy an Actor and Remove Actor from World\" src=\"docs/remove-actor-from-world.svg\" width=\"500\" /\u003e\n\n---\n\n### `GraphicsEngine`\n\nThe `GraphicsEngine` will take care of drawing anything that you add to a `World`. You generally\nwon't need to interact much with the `GraphicsEngine` or worry about what goes on in here. The\n`GameEngine` will take care of retrieving the `Actor` list from your `World` and loading their\ngraphical components into the `GraphicsEngine`. `Actor`s are drawn in the order that you add them to\nthe `World`, so you can maintain a pseudo-z ordering by taking advantage of this.\n\n#### `TGraphicObject`\n\n`TGraphicObject` is the base class for all graphical objects that can be displayed in the window.\nThe basic primitive shapes are `TRect` and `TOval` (which is actually a circle). These \nshapes can be drawn filled or outlined in any valid `java.awt.Color`. There is also support for \ndisplaying text using `TLabel` and images via the `GraphicsCtx` interface. Using these primitives\nand a `TGraphicCompound`, you can already do quite a lot! You may want to create a custom class\nthat extends from `TShape`, and in that case you can specify how the `GraphicsCtx` draws your \nshape. Override the `draw()` method and make use of the following methods:\n\n- `drawRect(Dimension, Color)`\n- `drawFilledRect(Dimension, Color)`\n- `drawCircle(Dimension, Color)`\n- `drawFilledCircle(Dimension, Color)`\n- `drawText(Point, String, Font, Color)`\n- `drawImage(Image)`\n- `drawImage(Image, Dimension)`\n\nMore than likely though, you will use a `Sprite` or `AnimatedSprite` for games. An indepth look \nat these classes can be found below.\n\nBelow is an overview of the entire Graphics class hierarchy and the methods for each class:\n\n![Overview of Graphics classes](docs/tgraphic-hierarchy.svg)\n\n\u003e **Warning**\n\u003e \n\u003e Currently, there is **no** support for getting the actual width or height from a `TLabel`.\n\nAll `TGraphicObject`s can have translation, rotation, and scale transforms applied to them. To \nset the translation of an object, use `setOrigin()`. To set the scale, use `setScale(x, y)`, and \nto set the rotation use `setRotation(angleDegrees, rotationOrigin)`. You can specify the origin\n(relative to the object's origin) about which a rotation applies; passing in (0, 0) will rotate\nthe object around its own origin.\n\n![Transforms](docs/transforms.svg)\n\n#### Using `TGraphicCompound` to compose a graphic\n\nThe `TGraphicCompound` is a composite primitive container that lets you group different \n`TGraphicObject`s and treat them as a single object. Following the GoF _Composite Pattern_, a \n`TGraphicCompound` can contain anything that is also a `TGraphicObject`, which includes other \n`TGraphicCompound`s. Each object added to a `TGraphicCompound` will apply its own transforms\n_after_ the transforms for the parent container have been applied, meaning that transforms will \naccumulate.\n\n#### `Sprite`\n\n- [ ] TODO: Create and use a `Sprite`\n\n#### `AnimatedSprite`\n\n- [ ] TODO: Create and update an `AnimatedSprite`\n\n---\n\n### `PhysicsEngine`\n\nThe `PhysicsEngine` is responsible for collision detection between `Actor`s in your world, creating\n`CollisionEvent`s, and notifying the `GameEngine` when two `Actor`s collide via a callback method.\nThe `GameEngine` sets this callback method to the `onCollision(CollisionEvent)` so your game can\nrespond to `CollisionEvent`s by overriding this method.\n\nTo detect collisions between any two `Actor`s, both `Actor`s need to have physical components\n(`TPhysicsComponent`) with collisions active (`hasCollisions`) and a `CollisionShape`.\n\n\u003e **Warning**\n\u003e\n\u003e Currently, only basic broad phase collision detection between two AABBs is supported. Please\n\u003e ensure you use a `CollisionRect` when building up a `TPhysicsComponent`.\n\nTo create a `TPhysicsComponent` for your `Actor`, use the `TPhysicsComponentBuilder` class. This\nclass follows [Bloch's Java Builder pattern](https://blogs.oracle. com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java),\nso works as you would expect:\n\n```java\nimport tengine.physics.*;\n\nclass BouncingBox extends Actor {\n    BouncingBox(Dimension dimension) {\n        this.dimension = dimension;\n        // other initialization...\n\n        physics = initPhysics();\n    }\n\n    TPhysicsComponent initPhysics() {\n        var boxPhysicsBuilder = new TPhysicsComponentBuilder();\n        var collisionRect = new CollisionRect(this.origin, this.dimension);\n\n        boxPhysicsBuilder.actor(this)\n                         .isStatic(false)               // Optional: is this Actor stationary? Default = true\n                         .collisionShape(collisionRect) // Optional: default = null\n                         .hasCollisions(true);          // Optional: default = false\n\n        return boxPhysicsBuilder.build();\n    }\n\n    // ...\n}\n```\n\nFor tile-based games or board games, you may find the `GridSquare` class a more useful \nand lightweight concept for collision detection simply by checking if they occupy the position \nin a grid.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftessapower%2Ftengine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftessapower%2Ftengine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftessapower%2Ftengine/lists"}