Demo update
-added 'dot' function to vector math -fixed Circle tool rendering -fixed some potentiall memory leaks -added 'collision' module which now separates ShootGrid from SpaceInvaders demo -separate some systems from demos to 'basic' module to better demos functionality sharing -slow down snake -added new graphics -BrickBreaker demo now works (without blocks breaking and with bugged collision detection) -some bug fixes
This commit is contained in:
parent
a0efa4e67d
commit
74179b4fc8
14 changed files with 871 additions and 468 deletions
|
|
@ -1,11 +1,18 @@
|
|||
module game_core.basic;
|
||||
|
||||
import bubel.ecs.core;
|
||||
import bubel.ecs.attributes;
|
||||
|
||||
import ecs_utils.math.vector;
|
||||
|
||||
import gui.attributes;
|
||||
|
||||
import ecs_utils.utils;
|
||||
|
||||
import app : launcher;
|
||||
|
||||
import bindbc.sdl;
|
||||
|
||||
struct CLocation
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
|
@ -56,4 +63,193 @@ struct CSelected
|
|||
mixin ECS.Component;
|
||||
|
||||
bool value = false;
|
||||
}
|
||||
|
||||
struct CInput
|
||||
{
|
||||
mixin ECS.Component;
|
||||
}
|
||||
|
||||
struct CDamping
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;
|
||||
|
||||
@GUIRange(0,9) byte value = 1;
|
||||
}
|
||||
|
||||
struct CVelocity
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;
|
||||
|
||||
vec2 value = vec2(0,0);
|
||||
}
|
||||
|
||||
struct CVelocityFactor
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;
|
||||
|
||||
vec2 value = vec2(1);
|
||||
}
|
||||
|
||||
|
||||
struct DampingSystem
|
||||
{
|
||||
mixin ECS.System!32;
|
||||
|
||||
struct EntitiesData
|
||||
{
|
||||
uint length;
|
||||
const (Entity)[] entity;
|
||||
@readonly CDamping[] damping;
|
||||
CVelocity[] velocity;
|
||||
}
|
||||
|
||||
float[20] damp = 0;
|
||||
|
||||
bool onBegin()
|
||||
{
|
||||
foreach(i;0..20)
|
||||
{
|
||||
damp[i] = powf((0.99 - cast(float)i * 0.01),launcher.delta_time*0.1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.velocity[i] = data.velocity[i] * damp[data.damping[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MoveSystem
|
||||
{
|
||||
mixin ECS.System!64;
|
||||
|
||||
struct EntitiesData
|
||||
{
|
||||
uint length;
|
||||
CLocation[] location;
|
||||
@readonly CVelocity[] velocity;
|
||||
@optional @readonly CVelocityFactor[] vel_factor;
|
||||
}
|
||||
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
if(data.vel_factor)
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.location[i] += data.velocity[i] * data.vel_factor[i] * launcher.delta_time;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.location[i] += data.velocity[i] * launcher.delta_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*System is responsible for movement of objects with CInput component.
|
||||
*In this example every entity has same speed when using movement system.
|
||||
*/
|
||||
struct InputMovementSystem
|
||||
{
|
||||
mixin ECS.System!32;
|
||||
|
||||
vec2 move_vector;
|
||||
|
||||
struct EntitiesData
|
||||
{
|
||||
uint length;
|
||||
//read only components can be marked with @readonly attribute or with const expression instead
|
||||
const (CInput)[] input;
|
||||
//components are treated as required by default
|
||||
//CLocation[] locations;
|
||||
CVelocity[] velocity;
|
||||
//CTexture[] textures;
|
||||
}
|
||||
|
||||
/**
|
||||
*onBegin gives opportunity to check keys once and call update on entities only when
|
||||
*one key is pressed.
|
||||
*/
|
||||
bool onBegin()
|
||||
{
|
||||
move_vector = vec2(0,0);
|
||||
if(launcher.getKeyState(SDL_SCANCODE_W))
|
||||
{
|
||||
move_vector += vec2(0,1);
|
||||
}
|
||||
else if(launcher.getKeyState(SDL_SCANCODE_S))
|
||||
{
|
||||
move_vector += vec2(0,-1);
|
||||
}
|
||||
if(launcher.getKeyState(SDL_SCANCODE_A))
|
||||
{
|
||||
move_vector += vec2(-1,0);
|
||||
}
|
||||
else if(launcher.getKeyState(SDL_SCANCODE_D))
|
||||
{
|
||||
move_vector += vec2(1,0);
|
||||
}
|
||||
|
||||
if(move_vector.x != 0 ||move_vector.y != 0)
|
||||
{
|
||||
move_vector = move_vector / sqrtf(move_vector.x * move_vector.x + move_vector.y * move_vector.y);
|
||||
return true;
|
||||
}
|
||||
//don't call system update because no key pressed
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*Update is called multiple times in one "manager.update()" call.
|
||||
*Number of "onUpdate" calls is count of buffers which must be updated during pass.
|
||||
*When multithreading is used, number of "onUpdate" calls can be greater due to fact that
|
||||
*JobSystem can split buffers for better data packing.
|
||||
*/
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
/*if(move_vector.x == 0)
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.textures[i].coords = vec4(0*px,80*px,48*px,32*px);
|
||||
}
|
||||
//return;
|
||||
}*/
|
||||
//move every entity using movement vector
|
||||
//if(move_vector.x != 0 || move_vector.y != 0)
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.velocity[i] += move_vector * launcher.delta_time * 0.005;
|
||||
if(data.velocity[i].x > 0.5)data.velocity[i].x = 0.5;
|
||||
else if(data.velocity[i].x < -0.5)data.velocity[i].x = -0.5;
|
||||
if(data.velocity[i].y > 0.5)data.velocity[i].y = 0.5;
|
||||
else if(data.velocity[i].y < -0.5)data.velocity[i].y = -0.5;
|
||||
//data.locations[i].x += move_vector.x * launcher.delta_time * 0.25;
|
||||
//data.locations[i].y += move_vector.y * launcher.delta_time * 0.25;
|
||||
//if(move_vector.x > 0)data.textures[i].coords = vec4(48*px,80*px,48*px,32*px);
|
||||
//else data.textures[i].coords = vec4(0*px,80*px,48*px,32*px);
|
||||
}
|
||||
/*else
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
data.velocity[i] = vec2(0,0);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
232
demos/source/game_core/collision.d
Normal file
232
demos/source/game_core/collision.d
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
module game_core.collision;
|
||||
|
||||
import bubel.ecs.attributes;
|
||||
import bubel.ecs.core;
|
||||
import bubel.ecs.std;
|
||||
|
||||
import ecs_utils.math.vector;
|
||||
|
||||
import game_core.basic;
|
||||
|
||||
|
||||
void registerCollisionModule(EntityManager* manager)
|
||||
{
|
||||
manager.registerDependency(ShootGridDependency);
|
||||
|
||||
manager.registerComponent!CShootGrid;
|
||||
manager.registerComponent!CShootGridMask;
|
||||
manager.registerComponent!CColliderScale;
|
||||
|
||||
manager.registerSystem!ShootGridManager(-80);
|
||||
manager.registerSystem!ShootGridCleaner(-101);
|
||||
}
|
||||
|
||||
enum ShootGridDependency = "ShootGridDependency";
|
||||
|
||||
struct CShootGrid
|
||||
{
|
||||
mixin ECS.Component;
|
||||
}
|
||||
|
||||
struct CShootGridMask
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;
|
||||
|
||||
ubyte value;
|
||||
}
|
||||
|
||||
struct CColliderScale
|
||||
{
|
||||
mixin ECS.Component;
|
||||
|
||||
alias value this;
|
||||
|
||||
vec2 value = vec2(16,16);
|
||||
}
|
||||
|
||||
struct ShootGrid
|
||||
{
|
||||
|
||||
~this() @nogc nothrow
|
||||
{
|
||||
if(nodes)Mallocator.dispose(nodes);
|
||||
if(masks)Mallocator.dispose(masks);
|
||||
}
|
||||
|
||||
struct Node
|
||||
{
|
||||
alias entity this;
|
||||
|
||||
EntityID entity;
|
||||
}
|
||||
|
||||
void create(ivec2 nodes_count, vec2 node_size)
|
||||
{
|
||||
this.size = nodes_count;
|
||||
this.node_size = node_size;
|
||||
inv_node_size = vec2(1.0/node_size.x, 1.0/node_size.y);
|
||||
nodes = Mallocator.makeArray!Node(nodes_count.x * nodes_count.y);
|
||||
masks = Mallocator.makeArray!ubyte(nodes.length);
|
||||
}
|
||||
|
||||
void mark(EntityID id, vec2 beg, vec2 end, ubyte mask)
|
||||
{
|
||||
ivec2 ibeg = cast(ivec2)(beg * inv_node_size);
|
||||
ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5);
|
||||
if(ibeg.x < 0)ibeg.x = 0;
|
||||
if(ibeg.y < 0)ibeg.y = 0;
|
||||
if(iend.x > size.x)iend.x = size.x;
|
||||
if(iend.y > size.y)iend.y = size.y;
|
||||
foreach(i; ibeg.y .. iend.y)
|
||||
{
|
||||
foreach(j; ibeg.x .. iend.x)
|
||||
{
|
||||
nodes[i * size.x + j] = id;
|
||||
masks[i * size.x +j] = mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
size_t size = nodes.length * EntityID.sizeof;
|
||||
memset(nodes.ptr, 0, size);
|
||||
memset(masks.ptr, 0, masks.length);
|
||||
}
|
||||
|
||||
bool test(out EntityID id, vec2 beg, vec2 end, ubyte mask)
|
||||
{
|
||||
ivec2 ibeg = cast(ivec2)(beg * inv_node_size);
|
||||
ivec2 iend = cast(ivec2)(end * inv_node_size + 0.5);
|
||||
if(ibeg.x < 0)ibeg.x = 0;
|
||||
if(ibeg.y < 0)ibeg.y = 0;
|
||||
if(iend.x > size.x)iend.x = size.x;
|
||||
if(iend.y > size.y)iend.y = size.y;
|
||||
foreach(i; ibeg.y .. iend.y)
|
||||
{
|
||||
foreach(j; ibeg.x .. iend.x)
|
||||
{
|
||||
uint index = i * size.x + j;
|
||||
if(nodes[index].id != 0)
|
||||
{
|
||||
if((masks[index] & mask) == 0)continue;
|
||||
id = nodes[index];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool test(out EntityID id, vec2 pos, ubyte mask)
|
||||
{
|
||||
ivec2 ipos = cast(ivec2)(pos * inv_node_size - 0.5);
|
||||
if(ipos.x < 0)ipos.x = 0;
|
||||
if(ipos.y < 0)ipos.y = 0;
|
||||
if(ipos.x >= size.x)ipos.x = size.x - 1;
|
||||
if(ipos.y >= size.y)ipos.y = size.y - 1;
|
||||
size_t index = ipos.y * size.x + ipos.x;
|
||||
if((masks[index] & mask) == 0)return false;
|
||||
if(nodes[index].id != 0)
|
||||
{
|
||||
id = nodes[index];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
vec2 inv_node_size;
|
||||
ivec2 size;
|
||||
vec2 node_size;
|
||||
Node[] nodes;
|
||||
ubyte[] masks;
|
||||
}
|
||||
|
||||
struct ShootGridCleaner
|
||||
{
|
||||
mixin ECS.System!1;
|
||||
|
||||
struct EntitiesData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ShootGrid* grid;
|
||||
|
||||
bool onBegin()
|
||||
{
|
||||
grid = gEM.getSystem!ShootGridManager().grid;
|
||||
if(grid != null)return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
if(grid)grid.clear();
|
||||
}
|
||||
}
|
||||
|
||||
struct ShootGridManager
|
||||
{
|
||||
mixin ECS.System!128;
|
||||
|
||||
mixin ECS.WritableDependencies!(ShootGridDependency);
|
||||
|
||||
struct EntitiesData
|
||||
{
|
||||
uint length;
|
||||
//uint thread_id;
|
||||
const (Entity)[] entity;
|
||||
@readonly CLocation[] locations;
|
||||
@readonly CShootGrid[] grid_flag;
|
||||
//@readonly CGuild[] guild;
|
||||
@optional @readonly CShootGridMask[] mask;
|
||||
@optional @readonly CScale[] scale;
|
||||
@optional @readonly CColliderScale[] collider_scale;
|
||||
}
|
||||
|
||||
ShootGrid* grid;
|
||||
|
||||
void onCreate()
|
||||
{
|
||||
//grid = space_invaders.shoot_grid;
|
||||
grid = Mallocator.make!ShootGrid;
|
||||
grid.create(ivec2(80,60), vec2(5,5));
|
||||
}
|
||||
|
||||
void onDestroy()
|
||||
{
|
||||
Mallocator.dispose(grid);
|
||||
}
|
||||
|
||||
// bool onBegin()
|
||||
// {
|
||||
// //if(!grid)return false;
|
||||
// //grid.clear();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
void onUpdate(EntitiesData data)
|
||||
{
|
||||
vec2[] scale;
|
||||
if(data.collider_scale)scale = cast(vec2[])data.collider_scale;
|
||||
else if(data.scale)scale = cast(vec2[])data.scale;
|
||||
else return;
|
||||
if(data.mask is null)
|
||||
{
|
||||
foreach(i; 0..data.length)
|
||||
{
|
||||
vec2 half_scale = scale[i] * 0.5;
|
||||
grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, ubyte.max);//cast(ubyte)(1 << data.guild[i].guild));
|
||||
}
|
||||
}
|
||||
else foreach(i; 0..data.length)
|
||||
{
|
||||
vec2 half_scale = scale[i] * 0.5;
|
||||
grid.mark(data.entity[i].id, data.locations[i] - half_scale, data.locations[i] + half_scale, data.mask[i]);//cast(ubyte)(1 << data.guild[i].guild));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue