brotherhood-of-recursive-descent / tankism

top down panzer game written in golang and ebiten
GNU Lesser General Public License v3.0
2 stars 0 forks source link
ebiten ecs gamedev golang

tankism Go Report Card

tankism

A top down panzer game written in Go.

devlog

01/2023 basic collision detection and resolution

see 7e08f530efb0aae9367d4a122715c353ebed2f8e

We have introduced collision detection and resolution systems. The former marks entities as having collided, using AABB 2d collision detection. The latter system is responsible for resolving the collision. At present we only look a velocity and make the colliding entity bounce back.

    entities := s.EntityManager.FindByComponents(components.CollisionType)

    for _, e := range entities {
        collision := e.GetComponent(components.CollisionType).(*components.Collision)

        if collision.Target.HasComponent(components.VelocityType) {
            v := collision.Target.GetComponent(components.VelocityType).(*components.Velocity)
            v.Intertia = -1   // bounce back
        }
        e.RemoveComponent(components.CollisionType)
    }

collision!

10/2022 first visual effect - particles

see a5bc3050ed29510dca9ceca1f3b589e18385e414

Yeah, we got some particles on the screen!

To create an emitter entity attach the emitter components

// see app/particles/main.go
redEmitter := p.EntityManager.NewEntity()
redEmitter.AddComponent(&components.ParticleEmitter{
    Color:        lib.ColorRed,

    Lifetime_min: 10,
    Lifetime_max: 100,

    Velocity_min:  1,
    Velocity_max:  1,

    Direction_min: 0,
    Direction_max: 90,
})
redEmitter.AddComponent(&components.Transform{X: ... })

particles!

10/2022 Relative Positioning

see 37193f42f3c4e66f7140bc9ea7614b6e01a35808

We implemented relative positioning of entities this evening. It is basically building a tree of transform components. With each entity only having max one transform and one transform only having one parent you get a scene graph.

To demonstrate relative positioning we decided to build a planet demo. As a bonus each planet as an emitting light attached to it.

// see app/position/main.go
//...
sun := s.EntityManager.NewEntity()
sunTransform := components.Transform{}
sun.AddComponents(&sunTransform, ... )

earth := s.EntityManager.NewEntity()
earthTransform := components.Transform{OffsetX: 300, OffsetY: 0}
earthTransform.AddParent(&sunTransform)

moon := s.EntityManager.NewEntity()
moonTransform := components.Transform{OffsetX: 150, OffsetY: 150}
moonTransform.AddParent(&earthTransform)

planets with lights, relative positioned

08/2022 SpriteAnimations making boom

see e1a38829ba41fd49d4c6c1dfdd1aefb79291aea4

The first action has landed. Loading sprites and rendering a list of images.

cycling through sprites

07/2022 Ambient Lighting + LightingTexture

see 5836834a79dc1b6507f941bedf472cae370f60b2

We finaly have some color dynamics in the game. Basically we draw every lighting source into a texture and then use a shader to merge everything.

package main

var AmbientColor vec4

func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
    // ( color of lightingmap + ambient color ) * destination color
    return (imageSrc0UnsafeAt(texCoord) + AmbientColor) * (color)
}

In the there are 3 lights in the scene. Pointlights on top of the barrel and the crate. In the right bottom corner there is a circle light which is bigger and more diffuse. To demonstrate the different effects you can achieve with different ambient light colors, we cycle through some color variations ...

cycling through ambient lights

05/2022 Adding first AI behaviour, the "observer"

see de790ae4dd2528046a20e21b7da82968f84523cd

This month we managed to draw the map, introduced ZIndex to the SpriteRenderSystem and added the first AI behaviour. A Tank rotates in the direction of the player.

enemy follows

04/2022 Making the tank move Asteroids style

see 4ef22abd3572bcd0eacb827181e0e58b160c6699

Finally some action on the screen. The tank is now keyboard controllable and moves across the screen.

tank moves

04/2022 Drawing a tank using Entity Component System(s) (ECS)

see 08892283bc67aabc710d610a02a6155b8704f25a

Today we implemented a basic ECS system placing a tank entity on the world, drawing a sprite and making the tank shake.

The generic ECS part can be found at /lib/ecs/* and the actual systems and components of tankism at /game/ecs/*.

Instead of having each game object being responsible for drawing itself and updating the game logic, there are systems that are responsible for the behavior. The data is stored in components. And the entities are a means of grouping components. This separation of concerns allows to decouple systems from other systems and entities from other entities. This is also an example of data-oriented design.

Below is the simple sprite render system:

type SpriteRenderer struct {
    EntityManager ecs.EntityManager
}

func (s *SpriteRenderer) Update() error { return nil }
func (s *SpriteRenderer) Draw(screen *ebiten.Image) {

    entities := s.EntityManager.FindByComponents(components.SpriteType, components.TranslateType)
    for _, e := range entities {

        op := &ebiten.DrawImageOptions{}

        translate := e.GetComponent(components.TranslateType).(*components.Translate)
        x := translate.X
        y := translate.Y
        scale := translate.Scale

        op.GeoM.Scale(scale, scale)
        op.GeoM.Translate(x, y)

        sprite := e.GetComponent(components.SpriteType).(*components.Sprite)
        img := sprite.Image
        screen.DrawImage(img, op)
    }
}

empty window

03/2022 Scene managment

see 360d4bac97ead6067e44a4f016ca182cae33db35

Tankism is a big for-loop. In order to put some structure to the code, we decided to have individual scenes managing certain aspects of the computer game. The Start-Scene is responsible for loading assets and the Game-Scene is responsible for managing the actual game.

Our initial thought is something along the lines:

scenes

03/2022 Folder structure

see 36813f5b94e36e06781b702731a05ff17800f7d1

Structuring go apps is a bit different than say Java packages. We tried to follow Bil Kennedy's advice of package-oriented design and came up with the following folder structure.

The main goal was to make the dependencies visible in the code utilizing the folder structure. Dependencies point downwards.

app/
├─ client/
│  ├─ gamescene/...
│  ├─ startscene/...
│  ├─ main.go
├─ server/
│  ├─ main.go
game/
├─ objects/
│  ├─ ...
│  ├─ tank.go
├─ ui/
│  ├─ ...
│  ├─ exitbutton.go
├─ colors.go
lib/
├─ ecs/...
├─ ui/...
├─ gameobject.go
media/
├─ images/...
├─ sounds/...

More about it at packagestructure

03/2022 First image drawn

see 43bf36088d9817475df42d7d8e318ac6970776e8

After more than 3 years of being dormant, we revived the idea of writing a top-down tank shooter. This time using the ebiten library. In our first remote session we managed to create an empty window :-)

empty window

development

game design

Modes

Other Features

Nice to have

Credits

Thank you https://kenney.nl for the amazing sprites and tilesheets: https://kenney.nl/assets/topdown-tanks-redux

Resources