Skip to content
This repository has been archived by the owner on May 21, 2021. It is now read-only.

Latest commit

 

History

History
390 lines (379 loc) · 22.2 KB

todo.org

File metadata and controls

390 lines (379 loc) · 22.2 KB

Initial release

  • [ ] Move out anything that is not a core system or component into examples
  • [ ] Provide tutorial example with graphics (devcards)
  • [ ] Component state validation? Not sure if this should be the responsibility of the game engine or not sinc ethere are many ways to handle validation and assertions
  • [X] Inspector integration? This can be a library, but could be useful for beginners
  • [ ] Lein template Everything needed with a basic setup of a game
  • [-] Better example game
    • [X] Nicer tile map
    • [X] Nicer sprites
    • [ ] Obligatory Flappy Bird?

FIX failing tests after refactor

Attack sprite-fn should be set someplace else

Maybe add a closure around stage and loader?

z-index for sprites

  • [X] Draw tiles first
  • [X] Save z-index to component state

Fix figwheel reloading not working for example.action_rpg

  • FSEvents bug on OSX where the figwheel watcher was not working
  • Move directory out of Dropbox fixed the issue

Tile map collision

  • [X] Add tiles to the broad/narrow collision detection? Decided not to so they could be used independently of each other
  • [X] Add metadata to tiles in tiled to denote whether they can be traversed
  • [X] Updated loading tilemap to include properties from tiled in attributes

Upgrade tileset

http://magiscarf.deviantart.com/art/Bunch-o-free-tiles-393827416

Rendering tile maps with larger tilesets/maps kills perf

Position component

  • There should be a position component instead of overloading the move component
  • Position system should come after collision and movement
  • What can change an entities position?
    • Movement
    • Camera
    • Something else?
  • Render system should cull sprites that have a position that is not on the screen

Tilemap switching

  • [ ] Load a new tilemap into the game state
  • [ ] Update tilemap collision system

Camera system

  • Controls where the camera is and the other systems should take that into account

Fireball should pass through impassable tiles?

Z-levels

  • If an entity is on a different z level then they should not interact
  • Collisions can take into account z level
  • Rendering should render things according to their z-level

Can’t move after colliding

Animation should handle an action and go back to the last one

  • Example: The user is hit while walking which should interupt the current walking animation and show the damage animation
  • Currently animations only change if an event is fired that is different than the current animation
  • Update animation actions to handle an argument in the message to play the last animation once it’s done

Handle large tileset maps to be render only what is needed

  • Tries to render the entire tile map a a texture which is 10,000 tiles for a 1600x1600 map
  • Based on the map location, slice the tilemap array
  • Render just the tiles in the bounds of map coordinance

In browser repl to eval into the game engine

Allow the user to modify and write new code in the browser while the game is running

Example

Modified snippet based on #clojurescript slack channel user escherize

(ns cljsfiddle.app
  (:require [cljs.js :refer [eval-str empty-state js-eval]]))

(defn my-eval [cljs-string]
  (eval-str (empty-state)
            (str "(ns cljs-user)"
                 cljs-string)
            'dummy-symbol
            {:ns 'cljs.user
             :eval js-eval
             :def-emits-var true
             :load (fn [& _] {:lang :clj :source "."})
             :context :statement
             ;; Safari support
             :static-fns true}
            (fn [{:keys [error value] :as x}]
              (if error
                (do
                  (def *er x)
                  (js/console.log (str error)))
                value))))

Ideas

  • Repl inside the game environment
  • Instead of event handlers, eval code
  • Source code in the game state so it can be viewed in the inspector You never know where you’ll be when you want to make that one change to see what happens
    • Parse the source code into an AST
    • Put the AST data in the inspector
    • Wrap it in a type and show a codemirror widget for the source
    • Edit the code, on submit, eval it and swap it into the state
  • Realtime collaboration of source code running in the browser
    • On changes, send it over a socket
    • Receive changes on a connected session
    • Add a system for multiplayer syncing
      • Server should be authoritative
      • Clients synchronize with the server
        • Latency based on the latency of the round trip to the server not to another player
      • To avoid latency, replay state (without rendering) up to the present by keeping a buffer of snapshots of game state
    • If source code is stored as state then it should be able to be sync’d
    • Networking for game programming http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/

In browser game IDE in ClojureScript

  • Inspect state
  • Alter running game state
  • Pause/rewind/fastforward
  • Code editor
  • Real time collaboration

Refactor the keyboard input system to emit events on change

Make utils.counters 1 element js arrays for better performance

Change any use of = or not= to identical?

Cache dynamic lookups in the game loop

  • There are many places that use dynamic lookups to get things like the list of entities, components, etc that could be cached
  • If you cache, need a way to invalidate the cache
  • Shouldn’t v8 be doing this already?

Remove anonymous functions

Anonymous functions are slower than def’d functions in js

  • [ ] Game loop creates a new anonymous function every time the game loop recurs
  • [ ] Component functions are anonymous functions
    • Also loose ability to have the repl rebind it dynamically once it’s in the game loop
  • [ ] System functions are anonymous

Fix all keys being captured by input system and it should only be in a certain scope

Test runner for js tests via PhantomJS

https://github.com/emezeske/lein-cljsbuild/blob/1.1.2/doc/TESTING.md

Self hosted cljs integration

Can we get the whole library and examples bootstrapped using self compiled cljs?

Remove mk- prefix from functions as per convention

Refactor attacking

  • [ ] Delete hitpoints namespace as it is not being used
  • [ ] Don’t overload collision events with data about damage
  • [ ] Query the damage amount from the component state of the entity that is being collided with

FX system

  • Global effects such as grayscale, waves, pixelate, color tint
  • Entity effects limited in scope to the entity

Add meta tags to component data for inspector to provide help text for fields

  • Example: (meta :doc “yo” {:a 1})
  • This doesn’t work with the evaluation model of praline because the parent would hold the information and need to pass it to the child

Sending out hit action event all enemy animations receive it?

  • CHECK FIRST: pixi reuses textures or frames
  • In animateable component:
    • It’s getting stuck on :hit-up and won’t revert back to :stand-down (when (= next-action :hit-up) (println “HIT UP” next-action current-animation-name))
    • Stack should have :stand-down in it, but only has :hit-up, this means that pushing the current action down the stack is not working

Rename components with -able

i.e animateable -> animation

Update the sprite/text renderer component to cull if the entity is outside of the viewport

Get rid of the event bus in favor of reading component state directly

  • Similar to React, instead of events to pass data, directly subscribe to other components
  • Can infer which components are coupled similar to how reagent does it
  • Can make component subscriptions explicit in the game wiring Instead of subscriptions, provide collection of component labels
  • Currently all subscriptions are of components for the same entity, but in theory it doesn’t have to be i.e global events
  • Having an event bus means component state can change, but downstream component state does not
  • Down stream would have to have logic for interpreting the state of the upstream component and they would thus be strongly coupled

Add subscriptions to other entities’ events

  • Currently all subscriptions are only for the entity which means another entity can not subscribe to the same messages
  • Would be useful to create say an entity with at text and move component that could follow around the player entity
  • Per component override

Auto generate a schema for component state

  • Derive the component-state schema
  • Explicitely or implicitely? Implicitely could use records and mk-state functions would have to return a record
  • When fields are updated in dev, perform a schema assertion (maybe using prismatic schema?)

Add assertions about the shape of the data in key areas of the framework

When dev-ing it’s nice to not have to deal with nil errors i.e calling nil as a function, nil values, etc

  • [ ] Systems can check that they are getting state that isn’t empty
  • [ ] Components can check that the component state matches expected
  • [ ] Events can validate event messages

Add position offset to text component to better position text around an entity

Test coverage

  • [X] Core framework
  • [ ] Components
  • [ ] Systems

Store entity component labels in a set instead of a list/vector for faster comparisons

multi-component-entities has to put the collection of component ids for each entity into a set before calling subset? on it

Spear attack doesn’t work unless pushing a direction key simultaneously

Use metadata to derive the boilerplate game state wiring

  • Instead of manually specifying all of the attributes of a system/component/entity use meta data
  • Example:
    • Component function has a component name of :foo: (defn cf {:component-name :foo} [] …) (defn component-name [f] (:component-name (meta (var component-fn)))) (component-name component-fn) => :foo
  • You can include functions in metadata too so we could use that to introspect the component’s name instead of hardcoding it, you would only need to require the component-fn which means the compiler will throw errors earlier
  • Specify dependencies of components for the purpose of catching errors earlier such as depending on a component state that does not exist
  • mk-component-fn can read the meta data and intelligently figure out what args to call the function with
    • Selected state ends up in the third argument to the component function (a hashmap)
    • This prevents having to write a function every time you want to read some other component-state, instead you could list it in the meta data
    • {:require-component-states [:moveable :collideable]}
    • {:subscriptions [:move :collision]}
    • Or with more sugar, a dsl for selecting state of the game/components etc {:require-state [[:game :stage] [:component :move] [:component :collision]]}

Add spinning to movement system

Batch all events at the system level? <2015-11-15 Sun>

  • This resulted in really big gains when doing collision detection where each entity can create more than one event
  • Batching events for the ai system brought much less improvement so there may be something inherent about the collision events that were more severe
  • Would be nice to only deal with events at the component fn level

Optimize ev/get-subscribed-events <2015-11-15 Sun>

According to profiler it’s really slow

Assets pipeline for asynchronously loading sounds/tiles/sprites/etc

  • Provide a nice abstraction for declaring pipeline of functions for loading assets asynchronously so that it doesn’t look like spaghetti
  • Integrate that into the mk-game-state function to keep the whole thing declarative

Add example of audio to the demo

Gameloop macro to inline the entire program into one function block

  • Read this somewhere that referencing a ton of functions all over the place is not good for performance or garbage collection
  • Write a macro that explodes all code into one massive function

input->interaction is non-deterministic

The output of the interaction hashmap is non-deterministic because it is iterating through a hashmap where ordering is not guaranteed. Need to iterate through only the accepted keycodes and check if the input-state shows the key is “on”. That way order is controlled by the caller

Moveable component-fn calls get-component-state twice for every entity

According to the compiler, the move component requires multiple get-component-state calls

Move rate should be calculated by Moveable and should have component state

  • Controllable should give the intended action based on user input i.e. :walk/run/attack :left/right etc
  • Another component should interpret that into a new screen position
  • Moveable needs to know if there is a collision before moving and intended position
  • Collideable needs to know the intended position of the character

Function that generates all the animation declarations in each direction

Key combinations from input

Add skip frames to animation declaration to control animation speed

Use transients for things that are going to be iterated over and only need a local mutable value.

Could work well for systems when iterating over them

Clean up tilemapping code

  • [ ] Add tests
  • [ ] Split up monster loops

Tiles that are non-traversable <2014-11-30 Sun>

Implement a tile map that checks for locations of entities that are collidable and sends an event if they are going to collide

  • [ ] Create a spatial grid based on the map location (offset based on the view port of the screen)
  • [ ] Put all tile collidable entities into their coordinates
  • [ ] Iterate over all occupied tiles
  • [ ] If they will be on a non-traversable tile, emit a tile collision event

Optimizations <2014-11-29 Sat>

  • Systems iterate over all entities that have the component and then each component function
  • Try to batch all the changes to the game-state in one shot
  • Try using the reducers library for zero allocation collection operations
  • Update component state and emit events takes up a significant amount of time number of hashmap ops = number of systems * number of entities with component * number of functions * number of events
  • Lots of analysis on clojurescript performance http://wagjo.github.io/benchmark-cljs/
  • [-] Use custom types using (.-a my-map) instead of keywords should be 3x faster <2014-11-30 Sun>
    • What about a macro that replaces get-in, assoc-in, update-in? Would need to always use our version of it which is dumb
    • Implement protocols for the custom type so that all the clojure map functions work with it
    • Underlying data structure will be a js array
    • [ ] Remove usage of assoc-in ./chocolatier/engine/ces.cljs:29: (assoc-in state [:scenes uid] system-ids)) ./chocolatier/engine/ces.cljs:68: (assoc-in state [:entities uid] component-ids)) ./chocolatier/engine/ces.cljs:86: (assoc-in state [:state component-id entity-id] val)) ./chocolatier/engine/ces.cljs:173: (assoc-in state [:components uid] {:fns wrapped-fns}))) ./chocolatier/engine/ces.cljs:211: (assoc-in state [:systems uid] system-fn))) ./chocolatier/engine/systems/collision.cljs:101: (assoc-in state [:state :spatial-grid] grid)))) ./chocolatier/engine/systems/events.cljs:71: (assoc-in state [:state :events :queue] {})) ./chocolatier/engine/systems/events.cljs:76: (assoc-in state [:state :events] {:queue {} :subscriptions {}})) ./chocolatier/engine/systems/input.cljs:48: (assoc-in state [:game :input] @KEYBOARD-INPUT)) ./chocolatier/engine/systems/tiles.cljs:42: (assoc-in state [:state :tiles] ./chocolatier/engine/systems/tiles.cljs:53: (assoc-in state [:state :tiles] tiles))) ./chocolatier/entities/enemy.cljs:28: (assoc-in [:state :renderable uid] init-render-state) ./chocolatier/entities/player.cljs:27: (assoc-in [:state :renderable uid] init-render-state)
    • [ ] Remove usage of get-in ./chocolatier/engine/ces.cljs:81: (or (get-in state [:state component-id entity-id]) {})) ./chocolatier/engine/systems/events.cljs:36: (let [subscriptions (get-in state [:state :events :subscriptions entity-id]) ./chocolatier/engine/systems/events.cljs:37: events (get-in state [:state :events :queue])] ./chocolatier/engine/systems/events.cljs:38: (mapcat #(get-in events (if (seqable? %) % [%])) subscriptions)))
    • [ ] Remove usage of update-in ./chocolatier/engine/systems/events.cljs:31: (update-in state [:state :events :subscriptions entity-id] conj selectors)) ./chocolatier/engine/systems/events.cljs:61: (update-in state (concat [:state :events :queue] selectors) conj event)))
    • This did not end up working because of the semantics of property access “.-” makes it impossible to construct at compile time without evaling symbols which means they can not be dynamically evalualted by putting thename of the key in a var for instance.
  • [ ] Batch game state changes
    • After every system take all of the changes from component entities and events and make the update in one shot
    • Uses many assoc-in
    • Should components operate on all entities at the same time? That would allow a single assoc-in to the game state from the accumulated component state that could be reduced in

Use a context buffer instead of writing all to one canvas

  • This should speed up the rendering of lots of sprites
  • Example code
    function onLoad() {
        // init stats
        var stats = new Stats();
        stats.getDomElement().style.position = 'absolute';
        stats.getDomElement().style.left = '0px';
        stats.getDomElement().style.top = '0px';
        document.body.appendChild( stats.getDomElement() );
        setInterval( function () { stats.update(); }, 1000 / 60 );
    
        // cache dom elements
        canvas = document.getElementById('my_canvas');
        context = canvas.getContext('2d');
        width = canvas.width;
        height = canvas.height;
        shipImage = document.getElementById('ship');
    
        // create canvas buffer
        canvasBuffer = document.createElement('canvas');
        contextBuffer = canvasBuffer.getContext('2d');
        canvasBuffer.width = 100;
        canvasBuffer.height = 100;
        contextBuffer.translate(50, 50); // so we can rotate about the center point
    
        // create lookup table for trig functions
        angleIncrement = Math.PI / 12;
        lookupTable = [];
        for (var i = 0; i < 5000; i++) {
            lookupTable[i] = {
                x: Math.cos(i) * width - 150,
                y: Math.sin(i) * height - 150
            };
        }
    
        // kick off the loop
        window.setInterval(update, 16);
    }
    
    // this is called using a 16 ms interval
    function update() {
    
        // draw transformed ship image to a canvas buffer
        contextBuffer.clearRect(0, 0, 100, 100);
        contextBuffer.rotate(angleIncrement);
        contextBuffer.drawImage(shipImage, 0, 0, 50, 50);
    
        // draw 5,000 ships
        for (var i = 0; i < 5000; i++) {
            var lookup = lookupTable[i];
            context.drawImage(canvasBuffer, lookup.x, lookup.y);
        }
    }
        

Use AABBTree (Axis aligned Bounding Box Tree) for collision detection

Make the input system emit an event

Currently it updates it’s component state but that’s it. SHould send an event to avoid other components querying it directly

Change mk-component to also handle subscribing to events

Change mk-system to also register it with a scene id

Reset the game height on screen resize

Re-implement fixed timestep loop

http://codeincomplete.com/posts/2013/12/4/javascript_game_foundations_the_game_loop/

During movement change the players map position <2014-03-23 Sun>

Branch: map-position Keep track of entities based on their map coordinates. Translate map coordinates into screen coordinates on render. This should help with the collision issues so that movement is decoupled from the :player entity

  • [ ] Add map-x and map-y to entities
  • [ ] Add offset x and y to background layer
  • [ ] On render apply offsets to the map and translate to screen changes
    • [ ] Tiles
    • [ ] Player
    • [ ] Monster

Function to translate screen coords to map coords

Entity to Tile collision detection

  • [ ] Boundary collisions (is a tile passable)
    • Check the players map position and find the nearest tile in the tile map
    • If the tile is passable then do nothing
    • If not then reset offset-x and offset-y to 0

Multiple hit boxes per entity

  • Entities should have body parts (multiple hit boxes)
  • Body parts have a hitbox and are checked during collision detection

Test with simulation

Makes a series of state changes to the game and returns the end state once all steps are completed Can be used for testing behavior visually and with real results

  • [ ] Record game state
  • [ ] Playback game state

[#A] Sound system

  • System that takes sound events, debounces, and plays sounds
  • Use howlerjs to manage playing clips

Draw ordering of entities to know which should be in front of what

When moving, keep the player in the center unless the border is < 1/2 the distance to the player then allow the player to move towards it