2DEntitySystem

Previous topic - Next topic

backslider

Hi guys,

here is my first verison of the 2DEntitySystem for GLBasic!
I added some demos where you can see how it can work.

It's really easy to use and I hope you like it!
The code isn't finished yet. If you have any cool ideas or functions then post it here and I will add it to the system.

Please comment it, use it in your games and mention me in the credits.  :good:

Cheers

[attachment deleted by admin]

erico

#1
just tried demo #2...

gasp cough, how can people come up with that! :o
Heck, so much math going on these days...

Amazing, I will give a go on the caanoo too!

Thanks Backslider, amazing work! :good:


Edit: when I re-run the code, the last entity seems to get stuck on the top left corner and wont move, I will give more tests on this and let you know, it must be on my messy side of things, of course.

backslider

Quote from: erico on 2012-Jun-18
Edit: when I re-run the code, the last entity seems to get stuck on the top left corner and wont move, I will give more tests on this and let you know, it must be on my messy side of things, of course.

Hoops, it was my mistake that the first box did not move.
I update them in a loop from i = 0 to 9 and set the speed = i. So the first had speed 0.  :good:

In the new version the problem is solved. ;)

It was only a test of your attention.  :whistle: =D

erico

Quote from: backslider on 2012-Jun-18
...It was only a test of your attention.  :whistle: =D

...and I failed! couse I´m sure I only noticed it on the second run... :O

Great stuff, must try this on caanoo soon just for the fun!
Must also try to understand what is going on...would come nice on a worm boss kind of thing :good:

backslider

If you have ideas for more demos, then tell me please!
I will try to implement them. :)

backslider

I implemented two new functions into my 2DEntitySystem:

- GetDistanceToEntity#: entity1%, entity2%
- GetDistanceToPoint#: entity%, x%, y%

The latest version is in the first post!

erico

Quote from: backslider on 2012-Jun-18
If you have ideas for more demos, then tell me please!
I will try to implement them. :)

I will think of a few ;/

Can you branch the objects? Like each one has a personal 3 or 4 mores objects going like a snake on each side?

backslider

Sorry, I don't know if I understand you right.

You want 3 or 4 snakes with some body parts, is that right?
If not, could you draw a sketch?  :nana:

erico

#8
Quote from: backslider on 2012-Jun-19
If not, could you draw a sketch?  :nana:

of course, here it goes.
It is like the snake demo, but each part carries a snake on its own.

Not sure if it is possible or if it could be a good demo.
Thing is similar to a fish skeleton.

But the appended snakes would be moving not on a hard way, but with a delay just as the main body.

[attachment deleted by admin]

backslider

#9
This idea is great!

I added a demo with a buggy "fish" who is following your mouse, but the idea with "bones" doesn't work now.  :P
Try the demo "Demo004_TheFish". ;)

Slim

#10
Hi,

Would there be someway to control the rate at which an entity MOVEs or ROTATEs?

e.g.   Move (entity, speed)

I mean more like time-based movement (delta T)?
This way it would be frame rate independent.

Slydog

#11
@Slim, @backslider

To move entities smoothly over time (frame rate independent), you could use my Tween library.
This could be added directly into the '2DEntitySystem' if you want (if backslider is interested), or used by itself manually.
(Keep in mind this was designed for 3D, for 2D, just ignore the 'z' component.)

Also, this could be used for rotation and scale (and colour if you want).

Here's how you use it:
Code (glbasic) Select
GLOBAL twn_player AS TTween
GLOBAL player_x%, player_y%

FUNCTION Player_TweenSet:  new_x%, new_y%
LOCAL tp[] AS TTween_Point
DIM tp[2]
tp[0].Set(  0, TWEEN_TYPE_CUBIC_INOUT, player_x, player_y, 0) // Start pos
tp[1].Set(1.0, TWEEN_TYPE_CUBIC_INOUT, new_x, new_y, 0)  // End pos
twn_player.New(3000, TRUE) // Total time should be 3 seconds, and Repeat this movement forever (TRUE)
twn_player.PathAdd(tp[0])
twn_player.PathAdd(tp[1])
ENDFUNCTION

FUNCTION GameLoop:
WHILE gameIsPlaying
twn_player.Update()
player_x = twn_player.value.x
player_y = twn_player.value.y
DRAWSPRITE sp_player, player_x, player_y
IF twn_player.status = TWEEN_FINISHED
// Tween has finished (but this is setup to run forever, so this will never be called in this instance)
ENDIF
WEND
ENDFUNCTION


Here's the 'Tween Path' library:
Code (glbasic) Select
//==============================================================================
// T w e e n   P a t h
//==============================================================================

//------------------------------------------------------------------ S t a t u s
CONSTANT TWEEN_OFF = 1
CONSTANT TWEEN_RUNNING = 2
CONSTANT TWEEN_FINISHED = 3

//--------------------------------------------------------- T w e e n  P o i n t
TYPE TTween_Point
p AS TVector
t
transition%

FUNCTION Set: time#, transition%, x, y, z
self.t = time
self.transition = transition
self.p.Set(x, y, z)
ENDFUNCTION

FUNCTION SetV: time#, transition%, v AS TVector
self.t = time
self.transition = transition
self.p = v
ENDFUNCTION
ENDTYPE

TYPE TTween
status%
duration
time
b_autoreset% = FALSE
value AS TVector
path[] AS TTween_Point

FUNCTION New: duration, auto_reset%=FALSE
self.duration = duration
self.status = TWEEN_RUNNING
self.time = 0
self.b_autoreset = auto_reset
self.value.Set(0,0,0)
DIM self.path[0]
ENDFUNCTION

FUNCTION Start:
self.value = self.path[0].p
ENDFUNCTION

FUNCTION Reset:
self.time = 0
self.status = TWEEN_RUNNING
ENDFUNCTION

FUNCTION IsRunning%:
IF self.status = TWEEN_RUNNING THEN RETURN TRUE
RETURN FALSE
ENDFUNCTION

FUNCTION Update:
LOCAL t
LOCAL v AS TVector

IF self.status <> TWEEN_RUNNING THEN RETURN

INC self.time, _timer
IF self.time >= self.duration
IF self.b_autoreset
DEC self.time, self.duration
ELSE
self.time = self.duration
self.status = TWEEN_FINISHED
ENDIF
ENDIF
t = self.time / self.duration

IF BOUNDS(self.path[], 0) > 0
PathCalc(t)
ENDIF
ENDFUNCTION



FUNCTION PathAdd: pp AS TTween_Point
DIMPUSH self.path[],   pp
ENDFUNCTION


FUNCTION PathCalc: t
LOCAL px%, ix%, jx%, p_last%
LOCAL points[] AS TVector
self.value.Set(0, 0, 0)
DIM points[4]
p_last = BOUNDS(self.path[], 0) - 1

px = -1
IF p_last = 0 THEN px = 0 // One point? Return first point
IF t = 0 THEN px = 0 // Very start? Return first point
IF t >= 1.0 THEN px = p_last // Very end? Return last point
IF px <> -1
self.value = self.path[px].p
RETURN
ENDIF

px = PathFindIndex(t)
IF px >= 0
ix = 0
FOR jx = px - 2 TO px + 1
IF jx < 0 // Index before first point? Set to first point
points[ix] = self.path[0].p
ELSEIF jx > p_last // Index beyond last point?  Set to last point
points[ix] = self.path[p_last].p
ELSE // Else set to actual point
points[ix] = self.path[jx].p
ENDIF
INC ix
NEXT
TweenTransition_SetVector(self.value, points[1], points[2], self.path[px].transition, (t - self.path[px-1].t) / (self.path[px].t - self.path[px-1].t))
ELSE
self.value = self.path[0].p
ENDIF
ENDFUNCTION

FUNCTION PathFindIndex%: t
LOCAL px%
FOR px = 1 TO BOUNDS(self.path[], 0) - 1
IF self.path[px].t >= t THEN RETURN px
NEXT
RETURN BOUNDS(self.path[], 0) - 1 // Default :: Return last point
ENDFUNCTION

ENDTYPE

// TVector TYPE used above
//------------------------------------------------------------------ V e c t o r
TYPE TVector
x
y
z

FUNCTION Set%: x, y, z
self.x = x
self.y = y
self.z = z
ENDFUNCTION
ENDTYPE


And here's the 'Tween Easing' library:
Code (glbasic) Select
//==============================================================================
// T w e e n   E a s i n g
//==============================================================================

//---------------------------------------------- T r a n s i t i o n   T y p e s
CONSTANT TWEEN_TYPE_LINEAR = 1
CONSTANT TWEEN_TYPE_SPRING = 2
CONSTANT TWEEN_TYPE_BOUNCE = 3
CONSTANT TWEEN_TYPE_QUAD_IN = 5
CONSTANT TWEEN_TYPE_QUAD_OUT = 6
CONSTANT TWEEN_TYPE_QUAD_INOUT = 7
CONSTANT TWEEN_TYPE_CUBIC_IN = 8
CONSTANT TWEEN_TYPE_CUBIC_OUT = 9
CONSTANT TWEEN_TYPE_CUBIC_INOUT = 10
CONSTANT TWEEN_TYPE_QUART_IN = 11
CONSTANT TWEEN_TYPE_QUART_OUT = 12
CONSTANT TWEEN_TYPE_QUART_INOUT = 13
CONSTANT TWEEN_TYPE_QUINT_IN = 14
CONSTANT TWEEN_TYPE_QUINT_OUT = 15
CONSTANT TWEEN_TYPE_QUINT_INOUT = 16
CONSTANT TWEEN_TYPE_SINE_IN = 17
CONSTANT TWEEN_TYPE_SINE_OUT = 18
CONSTANT TWEEN_TYPE_SINE_INOUT = 19
CONSTANT TWEEN_TYPE_EXPO_IN = 20
CONSTANT TWEEN_TYPE_EXPO_OUT = 21
CONSTANT TWEEN_TYPE_EXPO_INOUT = 22
CONSTANT TWEEN_TYPE_CIRC_IN = 23
CONSTANT TWEEN_TYPE_CIRC_OUT = 24
CONSTANT TWEEN_TYPE_CIRC_INOUT = 25
CONSTANT TWEEN_TYPE_BACK_IN = 26
CONSTANT TWEEN_TYPE_BACK_OUT = 27
CONSTANT TWEEN_TYPE_BACK_INOUT = 28

//============================================================================
// T R A N S I T I O N S                                       (Easing Curves)
//============================================================================
FUNCTION Tween_Get: transition_type%, p1, p2, time
LOCAL p
SELECT transition_type
CASE TWEEN_TYPE_LINEAR; p = TweenTransition_Linear   (p1, p2, time)
CASE TWEEN_TYPE_SPRING; p = TweenTransition_Spring    (p1, p2, time)
CASE TWEEN_TYPE_BOUNCE; p = TweenTransition_Bounce    (p1, p2, time)
CASE TWEEN_TYPE_QUAD_IN; p = TweenTransition_QuadIn    (p1, p2, time)
CASE TWEEN_TYPE_QUAD_OUT; p = TweenTransition_QuadOut   (p1, p2, time)
CASE TWEEN_TYPE_QUAD_INOUT; p = TweenTransition_QuadInOut (p1, p2, time)
CASE TWEEN_TYPE_CUBIC_IN; p = TweenTransition_CubicIn   (p1, p2, time)
CASE TWEEN_TYPE_CUBIC_OUT; p = TweenTransition_CubicOut  (p1, p2, time)
CASE TWEEN_TYPE_CUBIC_INOUT; p = TweenTransition_CubicInOut(p1, p2, time)
CASE TWEEN_TYPE_QUART_IN; p = TweenTransition_QuartIn   (p1, p2, time)
CASE TWEEN_TYPE_QUART_OUT; p = TweenTransition_QuartOut  (p1, p2, time)
CASE TWEEN_TYPE_QUART_INOUT; p = TweenTransition_QuartInOut(p1, p2, time)
CASE TWEEN_TYPE_QUINT_IN; p = TweenTransition_QuintIn   (p1, p2, time)
CASE TWEEN_TYPE_QUINT_OUT; p = TweenTransition_QuintOut  (p1, p2, time)
CASE TWEEN_TYPE_QUINT_INOUT; p = TweenTransition_QuintInOut(p1, p2, time)
CASE TWEEN_TYPE_SINE_IN; p = TweenTransition_SineIn    (p1, p2, time)
CASE TWEEN_TYPE_SINE_OUT; p = TweenTransition_SineOut   (p1, p2, time)
CASE TWEEN_TYPE_SINE_INOUT; p = TweenTransition_SineInOut (p1, p2, time)
CASE TWEEN_TYPE_EXPO_IN; p = TweenTransition_ExpoIn    (p1, p2, time)
CASE TWEEN_TYPE_EXPO_OUT; p = TweenTransition_ExpoOut   (p1, p2, time)
CASE TWEEN_TYPE_EXPO_INOUT; p = TweenTransition_ExpoInOut (p1, p2, time)
CASE TWEEN_TYPE_CIRC_IN; p = TweenTransition_CircIn    (p1, p2, time)
CASE TWEEN_TYPE_CIRC_OUT; p = TweenTransition_CircOut   (p1, p2, time)
CASE TWEEN_TYPE_CIRC_INOUT; p = TweenTransition_CircInOut (p1, p2, time)
CASE TWEEN_TYPE_BACK_IN; p = TweenTransition_BackIn    (p1, p2, time)
CASE TWEEN_TYPE_BACK_OUT; p = TweenTransition_BackOut   (p1, p2, time)
CASE TWEEN_TYPE_BACK_INOUT; p = TweenTransition_BackInOut (p1, p2, time)
ENDSELECT
RETURN p
ENDFUNCTION

FUNCTION TweenTransition_SetVector: v AS TVector, v1 AS TVector, v2 AS TVector, transition_type%, time
v.x = Tween_Get(transition_type, v1.x, v2.x, time)
v.y = Tween_Get(transition_type, v1.y, v2.y, time)
v.z = Tween_Get(transition_type, v1.z, v2.z, time)
ENDFUNCTION

// Transition :: L i n e a r
FUNCTION TweenTransition_Linear: p1, p2, t
t = Math_Clamp01(t)
RETURN p1 + ((p2 - p1) * t)
ENDFUNCTION

// Transition :: S p r i n g
FUNCTION TweenTransition_Spring: p1, p2, t
t = Math_Clamp01(t)
t = (QSIN(t * PI * (0.2 + 2.5 * t * t * t)) * POW(1.0 - t, 2.2) + t) * (1.0 + (1.2 * (1.0 - t)))
RETURN p1 + (p2 - p1) * t
ENDFUNCTION

// Transition :: B o u n c e
FUNCTION TweenTransition_Bounce: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
IF t < (1.0 / 2.75)
RETURN p2 * (7.5625 * t * t) + p1
ELSEIF t < (2.0 / 2.75)
DEC t, (1.5 / 2.75)
RETURN p2 * (7.5625 * t * t + 0.75) + p1
ELSEIF t < (2.5 / 2.75)
DEC t, (2.25 / 2.75)
RETURN p2 * (7.5625 * t * t + 0.9375) + p1
ELSE
DEC t, (2.625 / 2.75)
RETURN p2 * (7.5625 * t * t + 0.984375) + p1
ENDIF
ENDFUNCTION


// Transition :: Q u a d
FUNCTION TweenTransition_QuadIn: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
RETURN p2 * t * t + p1
ENDFUNCTION

FUNCTION TweenTransition_QuadOut: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
RETURN -p2 * t * (t - 2.0) + p1
ENDFUNCTION

FUNCTION TweenTransition_QuadInOut: p1, p2, t
t = t / 0.5
DEC p2, p1
IF t < 1.0 THEN RETURN p2 / 2.0 * t * t + p1
DEC t
RETURN -p2 / 2.0 * (t * (t - 2.0) - 1.0) + p1
ENDFUNCTION


// Transition :: C u b i c
FUNCTION TweenTransition_CubicIn: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
RETURN p2 * t * t * t + p1
ENDFUNCTION

FUNCTION TweenTransition_CubicOut: p1, p2, t
t = Math_Clamp01(t)
DEC t
DEC p2, p1
RETURN p2 * (t * t * t + 1.0) + p1
ENDFUNCTION

FUNCTION TweenTransition_CubicInOut: p1, p2, t
t = t / 0.5
DEC p2, p1
IF t < 1.0 THEN RETURN p2 / 2.0 * t * t * t + p1
DEC t, 2.0
RETURN p2 / 2.0 * (t * t * t + 2.0) + p1
ENDFUNCTION


// Transition :: Q u a r t
FUNCTION TweenTransition_QuartIn: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
RETURN p2 * t * t * t * t + p1
ENDFUNCTION

FUNCTION TweenTransition_QuartOut: p1, p2, t
t = Math_Clamp01(t)
DEC t
DEC p2, p1
RETURN -p2 * (t * t * t * t - 1.0) + p1
ENDFUNCTION

FUNCTION TweenTransition_QuartInOut: p1, p2, t
t = t / 0.5
DEC p2, p1
IF (t < 1.0) THEN RETURN p2 / 2.0 * t * t * t * t + p1
DEC t, 2.0
RETURN -p2 / 2.0 * (t * t * t * t - 2.0) + p1
ENDFUNCTION


// Transition :: Q u i n t
FUNCTION TweenTransition_QuintIn: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
RETURN p2 * t * t * t * t * t + p1
ENDFUNCTION

FUNCTION TweenTransition_QuintOut: p1, p2, t
t = Math_Clamp01(t)
DEC t
DEC p2, p1
RETURN p2 * (t * t * t * t * t + 1) + p1
ENDFUNCTION

FUNCTION TweenTransition_QuintInOut: p1, p2, t
t = t / 0.5
DEC p2, p1
IF (t < 1.0) THEN RETURN p2 / 2.0 * t * t * t * t * t + p1
DEC t, 2.0
RETURN p2 / 2.0 * (t * t * t * t * t + 2.0) + p1
ENDFUNCTION


// Transition :: S i n e
FUNCTION TweenTransition_SineIn: p1, p2, t
DEC p2, p1
RETURN -p2 * QCOS(t / 1.0 * (PI / 2.0)) + p2 + p1
ENDFUNCTION

FUNCTION TweenTransition_SineOut: p1, p2, t
DEC p2, p1
RETURN p2 * QSIN(t / 1.0 * (PI / 2.0)) + p1
ENDFUNCTION

FUNCTION TweenTransition_SineInOut: p1, p2, t
DEC p2, p1
//RETURN -p2 / 2.0 * (QCOS(PI * t / 1.0) - 1.0) + p1
RETURN QSIN(t * 360.0) * (p2/2.0)  + p1 + (p2/2.0)
ENDFUNCTION


// Transition :: E x p o
FUNCTION TweenTransition_ExpoIn: p1, p2, t
DEC p2, p1
RETURN p2 * POW(2.0, 10.0 * (t / 1.0 - 1.0)) + p1
ENDFUNCTION

FUNCTION TweenTransition_ExpoOut: p1, p2, t
DEC p2, p1
RETURN p2 * (-POW(2.0, -10 * t / 1.0) + 1.0) + p1
ENDFUNCTION

FUNCTION TweenTransition_ExpoInOut: p1, p2, t
t = t / 0.5
DEC p2, p1
IF (t < 1.0) THEN RETURN p2 / 2.0 * POW(2.0, 10.0 * (t - 1.0)) + p1
DEC t
RETURN p2 / 2.0 * (-POW(2.0, -10.0 * t) + 2.0) + p1
ENDFUNCTION


// Transition :: C i r c
FUNCTION TweenTransition_CircIn: p1, p2, t
t = Math_Clamp01(t)
DEC p2, p1
RETURN -p2 * (SQR(1.0 - t * t) - 1.0) + p1
ENDFUNCTION

FUNCTION TweenTransition_CircOut: p1, p2, t
t = Math_Clamp01(t)
DEC t
DEC p2, p1
RETURN p2 * SQR(1.0 - t * t) + p1
ENDFUNCTION

FUNCTION TweenTransition_CircInOut: p1, p2, t
t = t / 0.5
DEC p2, p1
IF (t < 1.0) THEN RETURN -p2 / 2.0 * (SQR(1.0- t * t) - 1.0) + p1
DEC t, 2.0
RETURN p2 / 2.0 * (SQR(1.0 - t * t) + 1.0) + p1
ENDFUNCTION


// Transition :: B a c k
FUNCTION TweenTransition_BackIn: p1, p2, t
LOCAL s = 1.70158;
t = Math_Clamp01(t)
DEC p2, p1
RETURN p2 * t * t * ((s + 1.0) * t - s) + p1
ENDFUNCTION

FUNCTION TweenTransition_BackOut: p1, p2, t
LOCAL s = 1.70158
t = Math_Clamp01(t) - 1.0
DEC p2, p1
RETURN p2 * (t * t * ((s + 1) * t + s) + 1.0) + p1
ENDFUNCTION

FUNCTION TweenTransition_BackInOut: p1, p2, t
LOCAL s = 1.70158
DEC p2, p1
t = t / 0.5
IF t < 1
s = s * 1.525
RETURN p2 / 2.0 * (t * t * ((s + 1.0) * t - s)) + p1
ENDIF
DEC t, 2
s = s * 1.525
RETURN p2 / 2.0 * (t * t * ((s + 1.0) * t + s) + 2.0) + p1
ENDFUNCTION


I hope this works as cut/pasted, and I included all referenced code (such as the TVector type, etc).
I had to remove some extra code that was experimental.
This was for tweening along a *curved* path using a Cosine curve, such as:
Code (glbasic) Select
//Beizer Curving Tween w/ Source: http://www.reflektions.com/miniml/template_permalink.asp?id=271
FUNCTION TweenTransition_Cosine: p0, p1, p2, p3, t
LOCAL a0, a1, a2, a3, t2
t2 = t * t
a0 = p3 - p2 - p0 + p1
a1 = p0 - p1 - a0
a2 = p2 - p0
a3 = p1
RETURN (a0 * t * t2 + a1 * t2 + a2 * t + a3)
ENDFUNCTION
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Slydog

#12
I realize this isn't what Slim requested, but tweening could be useful in a 2DEntitySystem.
I never actually checked out the entity code, but I ran the demos.  Very cool!

Does an entity have a direction setting?
(direction could be either an angle (from 0 to 360, or -180 to +180), or a vector (x, y))
If so, then Slim's request should be easy:  Move(entity, speed)
Just use GETTIMER() * speed.

The tweening code could be useful for these situations:
Code (glbasic) Select
TweenPosSet(entity, x, y, time, transition) // Move entity to x,y over 'time' ms, using transition specified, can be chained to allow a complex path
TweenRotationSet(entity, rotation, time, transition) // Rotate entity to 'rotation' degrees over time
TweenScaleSet(entity, scale, time, transition) // Scale entity to 'scale' size over time
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Slim

Hi SlyDog,

Thanks for the quick response.
It would be nice if it was just a matter of using GETTIMER() * speed.

@ backslider, Thanks for the entity system.

backslider

Your ideas of framerate independent moving and rotating is nice, but I don't know how to implement at the moment.
Could sb. of you implement it and comment the altered parts?

Then I will upload the new version.

I added a new demo called "Demo006_PointAndClickCar".
Here you can click somewhere into the window and the car will drive to your destination.