Author: Jared Rhine jared@wordzoo.com
Last update: April 2023
-
Install
go
using your preferred approach. -
Get the codebase from github:
go version git clone https://github.com/jaredrhine/gomertime cd gomertime go mod download
-
Run the gomertime server in one window/tab:
go run cmd/server/main.go
-
Run a gomertime client in another window/tab:
go run cmd/textclient/main.go
Text UI keys:
q
- quitescape
- quitcontrol+c
- quitspace
- paused
- dev/debug screen- arrow keys - move the viewport up/down/left/right
This codebase and line of work is undertaken with these goals in mind:
- This is a learning exercise, not an attempt to build a real game or community.
- ...so the whole codebase will be modern go (as written and evolved by a beginner).
- I want to learn go and in particular concurrency patterns using go channel idioms.
- I want to learn a bit about ECS (Entity/Component/System) architecture commonly used in games.
- ...so we'll recreate an ECS architecture from first principles, rather than using an existing ECS library.
- ...so we'll genericize the implementation to support two or more simulations within this one codebase.
- ...so we'll not start with an "archetype"-based ECS architecture, which is commonly done as a performance optimization to group entities for faster lookup.
- I want to support multiple-client and persistent server scenarios.
- ...so we'll move towards client/server architecture early
- ...and use JSON and WebSockets to learn HTTP-oriented go development, even though those probably aren't the right choices for a high-performance game framework. **5. Don't get lost in the details
- ...so we'll minimize following of ECS literature.
- ...so won't be afraid to take shortcuts around hard problems that real games need to solve.
- ...so I'll minimize testing because the code structure is likely to change repeatedly due to my unfamilarity with the problem space.
- ...so the UIs will start as text-only.
- ...so we'll fail fast (let errors happen) and minimize focus on correctness and robustness.
- ...so we'll move towards client/server architecture early
A "component" is a typed data bag, such as Position, Velocity, Food, Health that applies to one or more
An "entity" is really just a key/ID to represents an object or container which has multiple components.
A "system" is a code module which performs operations on subsets of components (say, "update position for all entities that have a velocity component")
Our first components will include:
- Position
- Velocity
- Health
Both Position and Velocity are modeled as a three-tuple of float values.
For Position, the 3-tuple represents the positive or negative X, Y, and Z cartesian coordinates of the entity located in a world centered on (0, 0, 0). The units for coordinate values are meters.
For Velocity, the 3-tuple represents a 3D vector, pointed in a specific direction. The units for vector values are meters per second.
-
Done
Project nameBasic go moduleWorld tick update loopTerminal clear and display shellPrototype core with keyboard, display, world tick integratedDetect keyboard press async'q' to exitPause keyDecide that components will belong to single entities, rather than being able to be shared between entitiesid generator (sequence for entity probably)ComponentPosition, velocity components stored as 3D floatEntityTemporary hard-coded world/entity creation/setupFake/inefficient ECS storage backend; defer archetype implementationDebug dump stateLookup console height/width on startPosition lookup/queryDynamic terminal height/widthDisplay representation of worldDifferent characters for different entitiesFirst test for position to text screen mappingDisplay viewport can move around worldMove WorldStore from controller to worldPosition updates based on velocity each tickSplit out main package from pkg codeLogging file is set by default, stderr avoidedWebSocket server framework, listens on socketText client is able to connect to websocket server, receive server tick, and displayLog level can be changed at start and at runtimeEnforce world boundaries during motion, worldXMin, worldXMax...Toroidal world option to wrap around worldText client is able to show updating server positionsMove position summary websockets push to array of objects, separate the x/y components while eliminating string keyText client is able to move around its viewport independently
-
Shortlist
- Add Acceleration component, including a cyclical function to watch an entity cycle back and forth. Ooo, and then circles.
- Client can connect to custom hostname/port. Client connection over tailscale tunnel confirmed.
- Server is headless
- Utility function to count number of neighbors
- Rewrite server using
time.NewTicker
- Split server and client fully
- Add Health component
- Find nearby entities
- Startup with origin in middle of screen. Display X,Y of cursor rather than
- Conway game of life rule implementation
-
Server/client
- Text client is able to send commands/updates to server
- Client registers its viewport, and server updates distinct caches for each client. Client at least receives some subset of every position in the world, as an optimization.
- Any clients is able to pause the server
- Clients are able to vote on pausing the server. When all clients have requested the server be paused, the server pauses until requested by any client to restart.
- Move to gob, flatbuffers, messagepack, protobufs on the wire
-
Display, UI
- Limit console vertical to a maximum height
- Camera follows entity
- Web canvas output
- Zoom in/out
- Text display dynamically resizes when window resizes
- Don't clear text screen every frame, only when needed. Minimize flashing.
- Dev console/palette
- Vertical/elevation support in text somehow
-
Core, game loop, golang
- Pass to examine and respond to errors; add error handling throughout
- Buildings which transform goods
- systems as goroutines. first: mover managing position
- Track wall clock time for stats
- loop over systems (for _, system := range world.Systems() ?)
- Cache viewport boundaries (xmin/xmax/ymin/ymax)
- Logging sophistication
- Genericize into IdType uint64
- Testing
- CI
- Benchmarking
- WebAssembly
- Optimization of PositionSummary. Perhaps only calculate all positions within a region
- Split code into multiple modules
- Data blob for component, independent of entity-specific data
- Lua engine embedded to write rules
- Rationalize the float/integer values used in position summary
-
Simulation
- Food consumption
- Pathfinding
- Force and acceleration
- Friction
- Destructability
- Optional rule/flag to not allow movement into same physical space?
- Toxins
- Light source
- Mass of iron
- Multiple components at same position (works now)
- Entity size/shape somehow. Entities can spread over multiple coordinates. Center of mass position.
- Edge of world handling. Bounce off walls. Hard stop.
- Fields, scalar (or vector) values at each position in the world. Magnetic, photon flux, gravity, ?
- Engineering with a company. Model dev motivation, daily work
- Reimplement "puffball" pro-forma ledger automation and modeling infrastructure as entities, and model ticks as the process over time.