Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411128 Posts in 69302 Topics- by 58376 Members - Latest Member: TitanicEnterprises

March 13, 2024, 07:22:41 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
  Show Posts
Pages: 1 2 [3] 4 5 ... 24
41  Developer / Technical / Re: The happy programmer room on: May 07, 2013, 02:12:25 PM
Some other multithreading C++11 proweness Wizard
So that doesn't block the main thread, but the task queue calls the second lambda on the main thread when the first lambda (load) is complete on whatever worker thread picked it up?

Will
42  Developer / Technical / Re: The happy programmer room on: April 19, 2013, 06:46:58 PM
I dunno, I might want my threading a bit more explicit than that - a static function rather than a member might make it easier to see where the split was between thread-local and shared stuff?

W
43  Developer / Technical / Re: The grumpy old programmer room on: April 17, 2013, 01:23:02 PM
A and B!
44  Developer / Technical / Re: What are you programming RIGHT NOW? on: April 16, 2013, 08:31:17 PM
Back in the day I learned a lot of BASIC programming skills writing pixel art programs for the Sinclair QL, when there really weren't any tools. I think if you're enjoying making a tool you should go it - it can be a better idea than making a game since your goals for an app are easier to focus. Especially if you're using it at the same time Smiley

I must admit I stopped when DPaint became available, but it was useful experience that fed into level editors and things later.

Currently programming: Switching my cave generator from creating walls with marching squares to working directly from the path network rep.

Will
45  Developer / Technical / Re: component based architecture on: April 13, 2013, 08:50:15 PM
Here's yet another take Sad It's not unlike Grozzlers, but more C++-ey and less granular.

* Entity has a transform, and you can refer to it with a salted pointer which is robust against entity deletion. World allocates entities from a pool.

* At init time, like Grozzler I provide World with a list of Systems. Each System implements one Facet (component) type. System allocates Facets from a pool. There's a standard system template which is useful for simple components, or you can write your own.

* At runtime, World updates each System in order, and each System updates its facets in pool (linear in memory) order. When Systems hook into third party code like Box2D, System::Tick will update the state of the third party world (or whatever) as necessary.

* Each Facet has a pointer to a block of constant "definition" data, and to create an Entity the World walks through a list of types and definition pointers.

* Facet code can send events to other entities - these are arbitrary structs with type IDs and get buffered up. After all the systems have updated they all get fed this frame's events, and ignore them or dispatch them to Facets as appropriate. This is the weakest link at the moment - it works, but it's high-latency and feels like it's barely enough.

* I also still have two crutches - you can get Facets from entities by type, and Facets can refer to each other within the same entity. This seems a bit messy and I'd be happier if I didn't need this - maybe if I can get a more solid design for events I won't?


I wasn't sure if anyone had really answered Tommo's question about where the game logic goes? In Unity this would be in a script component that you write yourself, which responds to events and does stuff with the built-in components in the entity.

I tend to have some game-specific C++ components for this kind of things, particularly for the player-controllable object where the messy logic goes. They do the high level gameplay glue code which would normally be in e.g. Player::Update(). I'd do the character animation switch there to start with.

Another real example: my simple balloon-ey game from the Versus compo has the following components, all C++ classes:

Built-in engine components:

* None Smiley

Add-on package components:

* box2d::Body - has velocity, physics body, collision shape
* box2d::Sensor - has collision shape, tracks things which overlap it
* box2d::Joint

* fx::Trigger - fires effects (sounds, particles in future) in response to cues.

Game-specific components:

* Bullet - does damage on impact, explodes, creates squib entities etc.
* Explosion - does damage, pushes bodies
* Health - takes damage, kills entity
* IPilot - provides yoke data
* PlayerPilot - is-a IPilot, maps controls to yoke
* Sprite - draws a simple sprite
* Weapon - fires out entities, handles ammo + reload timer
* Balloon - glues stuff together, maps yoke and internal state into body forces. Bit hacky.

Some of these are candidates for moving to a "game2d" helper package, like Bullet, Weapon and Health, and as they mature that might just happen.

NB: There's nothing to stop you cross-cutting the architecture and talking to the systems directly when pragmatism suggests it might helpful. E.g. I render the ropes (joints) which tie the balloon to the basket by iterating the JointSystem directly and drawing lines :p

Here's the definition data for the game to hopefully show how things are built up. This gets compiled into a memory image so all the quoted strings which refer to other things in the file, or to external files like assets, are just const pointers to ready-to-eat data on the C++ side.

Code:
SILO
{
BalloonShape
{
type= "sil::box2d::CircleShape"
radius = 3.0
density = 0.119
restitution = 0.1
category = 0x0008
mask = 0xffff
}

BasketShape
{
type = "sil::box2d::PolygonShape"
density = 2.0
restitution = 0.5
category = 0x0004
mask = 0xffff

vertex_count = 4
vertices
{
{ x = -0.75 y = -0.75 }
{ x = 0.75 y = -0.75 }
{ x = 0.75 y = 0.75 }
{ x = -0.75 y = 0.75 }
}
}

BombShape
{
type= "sil::box2d::CircleShape"
radius = 0.4
density = 4.0
restitution = 0.75
category = 0x0002
mask = 0xffff
}

CaltropShape
{
type = "sil::box2d::PolygonShape"
density = 4.0
restitution = 0.25
category = 0x0002
mask = 0xfffd

vertex_count = 3
vertices
{
{ x = 0.0 y = 0.3 }
{ x = -0.261 y = -0.15 }
{ x = 0.261 y = -0.15 }
}
}

GroundShape
{
type = "sil::box2d::PolygonShape"
category = 0x0001
mask = 0xffff

vertex_count = 4
vertices
{
{ x = -250.0 y = -12.5 }
{ x = 250.0 y = -12.5 }
{ x = 250.0 y = 12.5 }
{ x = -250.0 y = 12.5 }
}
}

GroundChainShape
{
type = "sil::box2d::ChainShape"
category = 0x0001
mask = 0xffff

chain
{
{ x = -500.0 y = 12.5 }
{ x = 500.0 y = 12.5 }
}
}

BlastShape
{
type = "sil::box2d::CircleShape"
category = 0x0001
mask = 0xffff
radius = 8.0
}

BalloonBody
{
type = "sil::box2d::BodyData"
linear_damping = 0.5
angular_damping = 1.0
shape = "BalloonShape"
}

BasketBody
{
type = "sil::box2d::BodyData"
linear_damping = 0.25
angular_damping = 0.5
shape = "BasketShape"
}

BombBody
{
type = "sil::box2d::BodyData"
linear_damping = 0.2
angular_damping = 0.2
shape = "BombShape"
}

CaltropBody
{
type = "sil::box2d::BodyData"
linear_damping = 0.2
angular_damping = 0.2
die_after_sleep = 2.0
shape = "CaltropShape"
}

GroundBody
{
type = "sil::box2d::BodyData"
shape = "GroundChainShape"
}

BombDropper
{
type = "WeaponData"
bullet = "Bomb"
angular_speed_variation = 3.0
max_ammo = 3.0
recharge_rate = 0.5
refire_delay = 0.5
fire_cue = 3
}

BombBullet
{
type = "BulletData"
explode_on_delay = 3.0
explode_on_impact = 0x0004
squib = "Explosion"
}

CaltropDropper
{
type = "WeaponData"
bullet = "Caltrop"
angular_speed_variation = 1.5
max_ammo = 6.0
recharge_rate = 1.0
refire_delay = 0.2
fire_cue = 4
}

CaltropImpactEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/caltrop_impact.wav"
volume = 0.1
}

CaltropEffects
{
type = "sil::fx::TriggerData"
actions
{
{ cue_id = 1 effect = "CaltropImpactEffect" }
}
}

CaltropBullet
{
type = "BulletData"
damage_on_impact = 0x0008
impact_damage_penetrating = 2.0
impact_cue = 1
}

BombExplosion
{
type = "ExplosionData"
min_impulse = 8.0
max_impulse = 25.0
lifetime = 0.2
}

BombExplosionSprite
{
type = "SpriteData"
sequence = "resources/graphics/blast.frm"
x_scale = 8.0
y_scale = 8.0
}

BombExplosionSensor
{
type = "sil::box2d::SensorData"
shape = "BlastShape"
}

MyBalloon
{
type = "BalloonData"
}

MyPilot
{
type = "PlayerPilotData"
}

MyHealth
{
type = "HealthData"
max_health = 40.0
}

BalloonSprite
{
type = "SpriteData"
sequence = "resources/graphics/balloon-0.frm"
x_scale = 1.05
y_scale = 1.05
}

BasketSprite
{
type = "SpriteData"
sequence = "resources/graphics/basket.frm"
x_scale = 1.05
y_scale = 1.05
}

BombSprite
{
type = "SpriteData"
sequence = "resources/graphics/bomb.frm"
}

CaltropSprite
{
type = "SpriteData"
sequence = "resources/graphics/caltrop.frm"
y_scale = -1.0
}

LeftSpring
{
type = "sil::box2d::JointData"
x1 = -2.5
y1 = -1.3
x2 = -0.75
y2 = 0.75
}

RightSpring
{
type = "sil::box2d::JointData"
x1 = 2.5
y1 = -1.3
x2 = 0.75
y2 = 0.75
}

MiddleSpring
{
type = "sil::box2d::JointData"
x1 = 0.0
y1 = -1.3
x2 = 0.0
y2 = 0.75
}

CaltropFireEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/caltrop_drop.wav"
volume = 0.2
}

BombFireEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/bomb_drop.wav"
}

BalloonBumpEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/balloon_bump.wav"
volume = 0.4
}

BasketBumpEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/basket_bump.wav"
volume = 0.1
}

BurnerEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/burner.wav"
loop = 1
volume = 0.1
intensity_rate = 1.2
}

BalloonEffects
{
type = "sil::fx::TriggerData"
actions
{
{ cue_id = 1 effect = "BasketBumpEffect" cooldown = 0.5 attached = 0 }
{ cue_id = 2 effect = "BalloonBumpEffect" cooldown = 0.5 attached = 0 }
{ cue_id = 3 effect = "BombFireEffect" cooldown = 0.0 attached = 0 }
{ cue_id = 4 effect = "CaltropFireEffect" cooldown = 0.0 attached = 0 }
{ cue_id = 5 effect = "BurnerEffect" cooldown = 0.0 attached = 1 }
}
}

Dirigible
{
type = "sil::game::Recipe"
ingredients
{
{ data = "BalloonBody" }
{ data = "BasketBody" offset { x = 0.0 y = -5.5 } }
{ data = "LeftSpring" anchors { value = 0 value = 1 } }
{ data = "RightSpring" anchors { value = 0 value = 1 } }
{ data = "MiddleSpring" anchors { value = 0 value = 1 } }
{ data = "MyBalloon" }
{ data = "MyHealth" }
{ data = "MyPilot" }
{ data = "BombDropper" anchors { value = 1 } }
{ data = "CaltropDropper" anchors { value = 1 } }
{ data = "BalloonSprite" anchors { value = 0 } }
{ data = "BasketSprite" anchors { value = 1 } }
{ data = "BalloonEffects" }
}
}

Bomb
{
type = "sil::game::Recipe"
ingredients
{
{ data = "BombBody" }
{ data = "BombBullet" }
{ data = "BombSprite" anchors { value = 0 } }
}
}

Caltrop
{
type = "sil::game::Recipe"
ingredients
{
{ data = "CaltropBody" }
{ data = "CaltropBullet" }
{ data = "CaltropSprite" anchors { value = 0 } }
{ data = "CaltropEffects" }
}
}

Explosion
{
type = "sil::game::Recipe"
ingredients
{
{ data = "BombExplosionSensor" }
{ data = "BombExplosion" }
{ data = "BombExplosionSprite" }
}
}

Ground
{
type = "sil::game::Recipe"
ingredients
{
{ data = "GroundBody" }
}
}
}


I think it's probably all a bit ( Well, hello there! Wink Well, hello there! ) over-engineered, but it's been a good experiment thus far. It feels like good practice to play with this stuff at home since I don't get to touch much gameplay code at work at the moment.

Will
46  Developer / Technical / Re: What are you programming RIGHT NOW? on: April 12, 2013, 10:24:19 PM
Resurrecting some cave generator code and making it data-driven so I can play with the settings 'live'.
47  Developer / Technical / Re: component based architecture on: April 12, 2013, 10:23:26 PM
tl;dr: keep it simple!
I don't disagree in principle, and there are a lot of ways to over-engineer with C++, but I'm finding components with the right granularity (quite large!) to be really useful.

I've been messing around with the implementation for a while, and adding bits and pieces whenever I want to write something new (usually in response to TIGS compos) and it's finally getting to the point where it all works.

e.g. I added sound support to my engine recently. I spent a while writing a basic low-level sound system which can play and loop and mix and so on. This bit doesn't know anything about game code.

One I had it, hooking it in the game code (with a new component) took minimal time and minimal boilerplate (which I was happy about) and actually setting up the game objects was all done in data (which I was delighted about). The data only thing isn't that important if you're a programmer-designer, but it's good if you work with non-programmers.

I definitely didn't start out with components though. I think it takes a few tries before you find an approach that works for you.

Will
48  Developer / Technical / Re: component based architecture on: April 11, 2013, 02:54:29 PM
I quite like that idea - generating and receiving standardised (for each component) input + output data and having it managed automatically by the systems. I *really* like the clear split between transient and retained data.

Do you have some kind of system to arbitrate what happens if e.g. two upstream components produce the same type of data, and the downstream component has to decide which to use?

My version of all this at the moment does something a bit like Timmy's - basically components can have pointers to other components. The presence of a pointer implies a dependency, and the entity will connect these together automatically (using reflection) when it's created. Generally this doesn't get used that much, but something like a controller will refer to a physics body in order to apply forces to it.

There are events as well, but I use those for things which don't happen every frame.

I suspect that I can probably use my event code (which queues up typed packets of data to be picked up by other components later) to implement exactly what xgalaxy describes and get rid of the explicit inter-component bindings. Hmm.

Will
49  Developer / Technical / Re: The grumpy old programmer room on: April 11, 2013, 02:40:09 PM
No problem, I thought - Mercurial saves reverted files as .orig, except in my case it hadn't.
It turns out this was because TortoiseHG has a little tiny checkbox which turns this feature off - must've accidentally checked it. So it wasn't the UTF-16.

Oh well, at least I now have full text diffs again.
50  Developer / Technical / Re: The grumpy old programmer room on: April 08, 2013, 08:26:56 PM
Bah, just lost an evening's work Sad

I'd done a too-big "hg add" and was reverting the various folders I'd added. I must've accidentally done "hg revert --all" through the GUI and lost my modified files as well.

No problem, I thought - Mercurial saves reverted files as .orig, except in my case it hadn't.

I *think* the issue was that unbeknownst to me, a dozen or so files in my source tree were UTF-16, which get treated as binary. I've changed these back to ASCII, fingers crossed this doesn't happen again.

It was of course these few files which contained all the changes I'd made. Next step: Recreate all those lost changes. It's usually quicker the second time...

W
51  Developer / Technical / Re: Sounds taking way too much space on: March 03, 2013, 12:06:09 PM
There's a mention near the top of that thread that TinySound converts all its sounds to 44KHz 16 bit PCM internally. I don't know if that means it's not really streaming your compressed file.

You could try saving the .ogg file in that format - if the size is similar to the memory usage you're seeing that might well be the problem?

[edit] I looked at the source and it appears that if you elect to stream a sound, it first converts it to TinySound's internal format and saves it as a temporary file, before playing back from that file again (!)

This seems a bit insane to me - the library also reads and writes the file one byte at a time, so streaming performance isn't going to be great even if it does work.

Will
52  Developer / Technical / Re: What are you programming RIGHT NOW? on: March 03, 2013, 12:00:05 PM
That looks awesome!

I'm still wiring up effects - I had "Effect" as an entity component but it didn't feel right. Now the entity component is "Trigger" and effects live in their own little system, independent of entities.

This makes it easier to do fire + forget stuff (like impacts) plus effects in general are much cheaper. You can still have an effect follow its entity if you want for continuous stuff.

Gasbags now makes different (horrible) noises when things happen. I think this is progress.

Will
53  Developer / Technical / Re: What are you programming RIGHT NOW? on: March 02, 2013, 02:22:53 AM
Glue code to connect my new sound support to the game engine. Decided to create an effects module as a container for other things later, like particles.

Really pleased with the data-driven stuff in my engine, all the code I wrote feels functional rather than boilerplate (I hate boilerplate) and without any further effort I can add

Code:
CaltropFireEffect
{
type = "sil::fx::EffectData"
clip = "resources/sound/rocket_explosion.wav"
cue = 1
}

to a text file to have it load & normalise the wave file, build it into the data library for the game, and play it when an entity using the component receives an event matching the cue.

I still need to think about the events a bit, they've only been used a little so far and I'm not sure if what I'm doing at the moment is really the right way to handle things. But the entities + components work great.

Will
54  Developer / Technical / Re: The happy programmer room on: February 28, 2013, 03:48:35 AM
portaudio ftw! \o/

\o/ indeed Smiley

Got mixing working now, and updated my stream interface so that streams can indicate that they're finished:

Code:
struct IStream
{
virtual bool Render( Sample *samples, Nat frames, Nat channels, Nat sample_rate ) = 0;
};

The virtual call isn't a big deal here because streams usually get asked to render a couple of thousand samples at a time, and it lets you compose streams without caring what they are. E.g. Mixer implements IStream, so you can have one mixer at the top level, or do submixes and things like that if you like.

Happy programmer Smiley

Will
55  Developer / Technical / Re: The happy programmer room on: February 26, 2013, 03:55:06 AM
I'm happy because I thought of a nice simple scheme to allow me to write audio packets from one thread and have PortAudio consume them from another. It even worked when I implemented it. Not bad for 80 lines of code. Although I might still need a memory barrier on the write side.

I now have most of the pieces of a lightweight sound system - output, wave loading, and a play head. Mixer next?

Will
56  Developer / Technical / Re: Tiny C++ Tweener. What do you think ? on: February 04, 2013, 06:32:04 PM
I've been thinking about it. I could just add the tweening functions as static functions in the CDBTweener class and use function pointers. But it would kind of destroy part of OO structure...

I think it wouldn't destroy it, it would supplement it. And IMO the "most correct" model for a plane curve (which is what tweens really are) is a function y = f(x).

So my suggestion would be not to get hung up on OO correctness (whatever that means) but it's definitely your library and your choice as to what fits in it Smiley

W
57  Developer / Technical / Re: Tiny C++ Tweener. What do you think ? on: January 31, 2013, 01:43:56 PM
Yeah, I can see the convenience factor. I'm not sure I'd want to let animation cross-cut other functionality like that, but it'd be great for some jobs.

That said, if you can package your tweening maths as free functions in the API and then build the tweening classes with them, you'd open the library up to a lot more use cases.

e.g. I would never use

Code:
float pos = CDBTweener::TWEQ_CUBIC.easeInOut(foo);

if I could use

Code:
float pos = CDBTweener::CubicEaseInOut(foo);

without the virtual call, temporary object, etc. It easier to type and maybe faster to run at the same time, and allows the library to serve "convenience" and "performance" users without compromising things for either one.

A version of the functions which just returned the curve for a [0, 1] float input would be great too.

My 2p,

Will
58  Developer / Technical / Re: Tiny C++ Tweener. What do you think ? on: January 27, 2013, 07:16:58 PM
I agree, that's a brilliant demo.  Beer!

One thing I don't really understand about this kind of code (which might be a lack of familiarity) is why you don't package it as a set of functions:

e.g.

Code:
float TweenLinear(float inT)
{
    return T;
}

float TweenSmooth(float inT)
{
    return 3*t*t - 2*t*t*t; // or whatever
}

// etc.

[edited because keyboard spasm posted too early...]

Then you can just apply the tween position to whatever you want using your own code, e.g. by calling lerp(a, b, SomeTween(t)). Having the framework manage all these objects seems quite heavyweight.

Or do you need to store a lot of state to do the tweening?

Thanks,

Will
59  Player / Games / Re: What are you playing? on: September 30, 2012, 05:57:23 PM
"My name is Eloi, and I am old..."

I love Cryo's facial work before they went fully CG. If you like the style, check out Dune and KGB. They do eyebrows like no-one else Smiley

Currently playing: Tokyo Jungle. Just awesome.
60  Developer / Technical / Re: The grumpy old programmer room on: August 27, 2012, 03:12:32 PM
That's really interesting, I hadn't thought of attempting that level of responsiveness - I assume you could feel the difference in play?

W
Pages: 1 2 [3] 4 5 ... 24
Theme orange-lt created by panic