Skip to content

Commit

Permalink
An easier way to define levels.
Browse files Browse the repository at this point in the history
  • Loading branch information
saolsen committed Sep 9, 2015
1 parent 2c0c291 commit df722fe
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 137 deletions.
4 changes: 3 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ the c version, currently only works on osx

[9/04/15] I did a ton of work to set up the debug stuff for rockets. I have a way to collect performance counters and a way to draw debug vectors and boxes on the scene. I really like the way the performance counters are collected. I still have to figure out some of the math to make that data useful and come up with a good way to present it. There are a lot of places I'd like to go with this stuff like replaying the game from a checkpoint with the recorded input and stepping through what happens, detecting slow frames. There's a lot I'd like to do with the debug drawing too like having a UI where I can turn on and off debug drawing for certain things on certain entities, stuff like that. All of that is a lot of work and it's hard to say if it's worth doing, I don't really know what sorts of things I'm really going to end up needing to debug. I have a good start though and will be adding stuff as I need it.

[9/08/15] Slowly getting back into this after a long weekend. I think I'm in the middle of debugging the collision detection but I just took a sec to print out better statistics for the performance counter stuff. This is going to be helpful later but I will probably display it as a searchable table in game rather than interact with it as a console log. That way I can corrilate it to other debug info and replay slow frames, etc.
[9/08/15] Slowly getting back into this after a long weekend. I think I'm in the middle of debugging the collision detection but I just took a sec to print out better statistics for the performance counter stuff. This is going to be helpful later but I will probably display it as a searchable table in game rather than interact with it as a console log. That way I can corrilate it to other debug info and replay slow frames, etc.

[9/09/15] I was thinking about the game and all the things I want to do for it but what's really important is that it's fun and the puzzles are good. If the puzzles aren't good and the game isn't fun then any other platform / debugger / story / graphics / ui doesn't even matter so I need to refocus on making good levels. I started that by adding an easier way to programattically build levels. I introduced a new data structure to represent a level specification. There are going to be a rich set of functions to build these up. Then when the user loads a level these specs are translated into actuial entities. In that way we make little prefabs (using a unity term) that let me specify a ship is at this location, but when the level is loaded the actual collision geometry and stuff is set up correctly. These are live editable the same way my other code is so I should be able to iterate on level design this way. This will let me really get to work on the core of the game which has to be solid or everything else is a waste of time.
115 changes: 36 additions & 79 deletions rockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,40 @@
#include "rockets.h"
#include "rockets_nodes.c"

// @TODO: Maybe move this to rockets_entitites.c
void
entity_add_flags(Entity* entity, uint32_t flags)
{
entity->flags |= flags;
}

bool
entity_has_flags_set(Entity* entity, uint32_t flags)
{
return entity->flags & flags;
}

/* Gamestate */

Entity*
push_entity(GameState* gamestate)
{
Entity* new_entity;
if (gamestate->first_free_entity) {
new_entity = gamestate->first_free_entity;
gamestate->first_free_entity = new_entity->next_entity;
} else {
assert(gamestate->num_entities < ARRAY_COUNT(gamestate->entities));
new_entity = gamestate->entities + gamestate->num_entities++;
}

// @TODO: Maybe clear the entity out.
return new_entity;
}

// @NOTE: gamestate stuff needs to go before rockets_levels because we call push_entity.
#include "rockets_levels.c"

/* Rendering */

// @TODO: Pull out the GUI debug drawing stuff too.
Expand Down Expand Up @@ -417,38 +451,6 @@ nodestore_eval_thrusters(const NodeStore* ns, const Entity* ship)
return out_thrusters;
}

/* Entities */

void
entity_add_flags(Entity* entity, uint32_t flags)
{
entity->flags |= flags;
}

bool
entity_has_flags_set(Entity* entity, uint32_t flags)
{
return entity->flags & flags;
}

/* Gamestate */

Entity*
push_entity(GameState* gamestate)
{
Entity* new_entity;
if (gamestate->first_free_entity) {
new_entity = gamestate->first_free_entity;
gamestate->first_free_entity = new_entity->next_entity;
} else {
assert(gamestate->num_entities < ARRAY_COUNT(gamestate->entities));
new_entity = gamestate->entities + gamestate->num_entities++;
}

// @TODO: Maybe clear the entity out.
return new_entity;
}

/* GUI stuff */

// draws a button there, if there was a click of it this frame returns true
Expand Down Expand Up @@ -926,52 +928,7 @@ gui_nodes(GUIState* gui, NodeStore* ns)

void
setup_level(GameState* state) {
// @NOTE: zero entities out.
memset(state->entities, 0, sizeof(Entity) * ARRAY_COUNT(state->entities));
state->num_entities = 0;
state->first_free_entity = NULL;

Entity* ship = push_entity(state);
ship->type = EntityType_SHIP;
ship->position = v2(300, 99);
ship->rotation = 0;

entity_add_flags(ship, EntityFlag_COLLIDES);

/* CollisionRect* s1 = &ship->collision_pieces[ship->num_collision_pieces++]; */
/* s1->offset = v2(-10, -15); */
/* s1->size = v2(20, 40); */
CollisionRect* s2 = &ship->collision_pieces[ship->num_collision_pieces++];
s2->offset = v2(-20, -25);
s2->size = v2(15, 30);
/* CollisionRect* s3 = &ship->collision_pieces[ship->num_collision_pieces++]; */
/* s3->offset = v2(5, -25); */
/* s3->size = v2(15, 30); */

Entity* goal = push_entity(state);
goal->type = EntityType_GOAL;
goal->position = v2(500, 600);
goal->rotation = 0;

entity_add_flags(goal, EntityFlag_COLLIDES);

CollisionRect* g = &goal->collision_pieces[goal->num_collision_pieces++];
g->offset = v2(-10, -10);
g->size = v2(20, 20);

state->current_level = 1;

Entity* test_block = push_entity(state);
test_block->type = EntityType_BOUNDRY;
test_block->position = v2(300, 300);
test_block->rotation = 0;

entity_add_flags(test_block, EntityFlag_COLLIDES);

/* CollisionRect* r = &test_block->collision_pieces[test_block->num_collision_pieces++]; */
/* r->offset = v2(30, 0); */
/* r->size = v2(50, 50); */

load_level(state, 1);
state->status = RUNNING;
}

Expand Down Expand Up @@ -1273,7 +1230,7 @@ game_update_and_render(void* gamestate,
}
nvgRestore(vg);

debug_print_records();
/* debug_print_records(); */
}

const gg_Game gg_game_api = {
Expand Down
99 changes: 42 additions & 57 deletions rockets.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
/*
@TODO: Known Bugs, fix these ASAP.
@TODO: @IMPORTANT: Known Bugs, fix these ASAP.
* The collision detection stuff isn't working properly.
@TODO: next steps.
* Better debug setup. Logging, performance counters, easier debug visualizations.
(watch casey on this stuff today)
* Debug the collision detection code.
* Use collisions for gameplay stuff.
* Start really making some levels and play with the game mechanics. Any platform work before having
a viable game and levels is just wasted.
What do I need for a minimum viable level system.
- Track what level you on.
- Build levels from a simple description.
- Compile description into scene entities.
Don't make a website or design a ui or anything until you got a bunch of fun levels.
If I make an actualy fun game, I can actually make money on it if I push it.
But if the game isn't fun, it's never gonna happen.
@TODO: Stuff I think I should do.
* Debug code.
I really want more debug code. I was leaning on it heavily for the collision detection stuff. I
Expand All @@ -22,15 +39,27 @@
advanced version that handles rotations too so maybe I should just invest in debug tools a
bit more.
I really really need to start making levels and playing with the mechanics. I need to figure
out if there is enough depth in the mechanic to make meaningful puzzles and a fun game. This
is the real make or break point for the game, it has to have good puzzles. After that I also
need a good story.
* Rendering
Pull out any raw nanovg calls. Put all that behind a rendering API. Make the debug stuff part
of debug utils and the drawing stuff abstracted so I can ditch nanovg later.
Real renderer is probably going to be a lot more complex. I want a nice looking game.
For now this vector stuff works fine.
Porting
* Once I really have a game it would be great to get it running on a lot of platforms.
- This probably means abstracting the rendering api which would also probably mean writing the
rendering from scratch, casey style but adding hardware acceleration. Can port this to any tablet
that has a rendering api and those that don't even.
I'll keep saying and believing this. HMH is the absolute best thing I've ever watched.
Just keep watching it!
@TODO: next steps.
* Better debug setup. Logging, performance counters, easier debug visualizations.
(watch casey on this stuff today)
* Debug the collision detection code.
* Use collisions for gameplay stuff.
*/
#ifndef _game_h
#define _game_h
Expand All @@ -41,6 +70,8 @@
#include "rockets_math.h"
#include "rockets_debug.h"
#include "rockets_nodes.h"
#include "rockets_entities.h"
#include "rockets_levels.h"

// GUI stuff
// @TODO: rockets_gui
Expand Down Expand Up @@ -89,56 +120,6 @@ typedef struct {

typedef enum { BS_NAH, BS_HOVER, BS_CLICK } ButtonState;

// Entities
// @TODO: rockets_entities

typedef enum {EntityType_NAH,
EntityType_SHIP,
EntityType_BOUNDRY,
EntityType_GOAL} EntityType;

typedef enum {EntityFlag_COLLIDES = (1 << 0)} EntityFlag;

// Start with a special case of just having all collision geometry be rectangles.
// For now going to have rotation be about the entitiy's position. Makes math easy
// and seems to make sense for things that are going to move around.

// @NOTE: This is basically the same as bounding box but the names are different because this is
// a different way to describe a rectangle.
typedef struct {
union {
struct {float x, y;};
V2 offset;
};
union {
struct {float width, height; };
V2 size;
};
} CollisionRect;


// How do I determine entity equality? Just pointer equality?
typedef struct entity_ {
EntityType type;
uint32_t flags;

V2 position;
int rotation;
Thrusters thrusters;

V2 velocity;

// @TODO: I think I should probably use a memory area for this instead of allocating
// 10 slots for every entity. Many will just have 1 piece and lots won't have any.
// If I have a memory area I can track memory usage in it too.
// Also could let me pack all collision pieces in a single array which might be dope
// if I want to SIMD my collision checking.
int num_collision_pieces;
CollisionRect collision_pieces[10];

struct entity_* next_entity;
} Entity;

typedef struct entity_pointer_ {
Entity* entity;
struct entity_pointer_* next_entity;
Expand All @@ -163,6 +144,10 @@ typedef struct {
Entity* first_free_entity;
} GameState;

// NAH.
// @TODO: These can go in rockets_entities.c if I make that.
void entity_add_flags(Entity* entity, uint32_t flags);
bool entity_has_flags_set(Entity* entity, uint32_t flags);
Entity* push_entity(GameState* gamestate);


#endif
52 changes: 52 additions & 0 deletions rockets_entities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#ifndef _rockets_entitites_h
#define _rockets_entitites_h

// Entities
typedef enum {EntityType_NAH,
EntityType_SHIP,
EntityType_BOUNDRY,
EntityType_GOAL} EntityType;

typedef enum {EntityFlag_COLLIDES = (1 << 0)} EntityFlag;

// Start with a special case of just having all collision geometry be rectangles.
// For now going to have rotation be about the entitiy's position. Makes math easy
// and seems to make sense for things that are going to move around.

// @NOTE: This is basically the same as bounding box but the names are different because this is
// a different way to describe a rectangle.
typedef struct {
union {
struct {float x, y;};
V2 offset;
};
union {
struct {float width, height; };
V2 size;
};
} CollisionRect;


// How do I determine entity equality? Just pointer equality?
typedef struct entity_ {
EntityType type;
uint32_t flags;

V2 position;
int rotation;
Thrusters thrusters;

V2 velocity;

// @TODO: I think I should probably use a memory area for this instead of allocating
// 10 slots for every entity. Many will just have 1 piece and lots won't have any.
// If I have a memory area I can track memory usage in it too.
// Also could let me pack all collision pieces in a single array which might be dope
// if I want to SIMD my collision checking.
int num_collision_pieces;
CollisionRect collision_pieces[10];

struct entity_* next_entity;
} Entity;

#endif
52 changes: 52 additions & 0 deletions rockets_levels.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// @NOTE: This is just so flymake doesn't get mad.
#include "rockets.h"

// Loads the entites for a given level.
// @TODO: Handle boarders.
void load_level(GameState* state, int level_number)
{
memset(state->entities, 0, sizeof(Entity) * ARRAY_COUNT(state->entities));
state->num_entities = 0;
state->first_free_entity = NULL;
state->current_level = level_number;

LevelSpec spec = get_level_spec(level_number);

for (int i = 0; i < spec.num_entity_specs; i++) {
EntitySpec entity = spec.entity_specs[i];

switch(entity.type) {
case(ES_PLAYER_SHIP): {
Entity* ship = push_entity(state);
ship->type = EntityType_SHIP;
ship->position = entity.position;
ship->rotation = 0;

entity_add_flags(ship, EntityFlag_COLLIDES);

CollisionRect* s1 = &ship->collision_pieces[ship->num_collision_pieces++];
s1->offset = v2(-10, -15);
s1->size = v2(20, 40);
CollisionRect* s2 = &ship->collision_pieces[ship->num_collision_pieces++];
s2->offset = v2(-20, -25);
s2->size = v2(15, 30);
CollisionRect* s3 = &ship->collision_pieces[ship->num_collision_pieces++];
s3->offset = v2(5, -25);
s3->size = v2(15, 30);

} break;
case(ES_GOAL): {
Entity* goal = push_entity(state);
goal->type = EntityType_GOAL;
goal->position = entity.position;
goal->rotation = 0;

entity_add_flags(goal, EntityFlag_COLLIDES);

CollisionRect* g = &goal->collision_pieces[goal->num_collision_pieces++];
g->offset = v2(-10, -10);
g->size = v2(20, 20);
} break;
}
}
}
Loading

0 comments on commit df722fe

Please sign in to comment.