Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Function pointer uses in game object behaviors....
#1
I just made this example so that newer FB users don't get the problem we have at using a lot of select cases. Enjoy!

Code:
' Using sub/function pointers
' to make objects easier to handle
' I've made this example because I am
' amazed by the fact that not a lot of you
' are exploiting this fine FB capability
' to make games a lot easier to code.
' ie:
' Instead of a bunch of select case statements
' and deciding object movement via an ID system,
' function/sub pointers w/in TYPES should be used.
' Pros:
' 1. code can be easily modularized
' 2. object AI can be easily debugged because it resides
'      in a single sub/funk
' 3. less cluttered code
' 4. object behavior can be easily modified or changed
' 5. code is not as error prone as a buch of select cases
'
' cons: Nothing I can think of ;*)
'
' Relsoft 2005
' Rel.Betterwebber.com
'
'

defint a-z
option explicit

'screen dimensions for screen 14
#define SCREEN_W 320
#define SCREEN_H 240
#define SCREEN_W_MID SCREEN_W shr 1
#define SCREEN_H_MID SCREEN_H shr 1

'misc defines
#define PI  3.141593
#define NUM_PARTS  60

'our particle structure
Type ParticleType
     x             as single            'bunch of public data
     y             as single
     xv             as single
     yv             as single
     angle         as integer
     radius         as integer
     color         as integer
     move_sub     as sub (byref pt as ParticleType) 'sub pointer for object behavior
End Type

'declare before use
declare sub move_circle_right (byref pt as ParticleType)
declare sub move_circle_left (byref pt as ParticleType)

'our particles
dim Parts(NUM_PARTS - 1) as ParticleType


'320*240, 32 bit, 2 pages
SCREEN 14, 32, 2

'misc counter
dim i as integer

'put values to each particle
for i = 0 to NUM_PARTS - 1
    Parts(i).x = 0
    Parts(i).y = 0
    Parts(i).xv = 0
    Parts(i).yv = 0
    Parts(i).angle = int(i * 360* 3 / NUM_PARTS)
    Parts(i).Radius = int(i * NUM_PARTS/25)
    Parts(i).color = cint(rnd * 255) shl 16 or cint(rnd * 255) shl 8 or cint(rnd * 255)
    'lazy way to assign sub pointers :*)
    'if odd then rotate right, left otherwise
    if i and 1 then
        Parts(i).move_sub = @move_circle_right
    else
        Parts(i).move_sub = @move_circle_left
    end if
next i

do

    SCREENSET 1, 0          'set work page for double buffering
    cls                         'clear the screen
    'do the objects
    'notice how I only call the sub pointer
    'and it does everything for me? ;*)
    for i = 0 to NUM_PARTS - 1
        Parts(i).move_sub(Parts(i))
        circle (Parts(i).x, Parts(i).y), 5, Parts(i).color
    next i
    wait &h3da, 8            'vsynch
    SCREENCOPY                'flip

loop until inkey$<>""



end


'only 2 subs for clarity

'rotates right
sub move_circle_right (byref pt as ParticleType)
    dim a as single
    pt.angle = (pt.angle + 1)
    if pt.angle > 359 then pt.angle = 360 - pt.angle
    a = pt.angle * PI /180
    'add some wavy animation to the circles
    'displacement = angle * 2PI / steps
    pt.radius = pt.radius + (5) * sin((pt.angle) * 2 * PI/30)
    pt.x = SCREEN_W_MID + cos(a) * pt.radius
    pt.y = SCREEN_H_MID + sin(a) * pt.radius
end sub

'rotates left
sub move_circle_left (byref pt as ParticleType)
    dim a as single
    pt.angle = (pt.angle - 1)
    if pt.angle < 0 then pt.angle = pt.angle + 360
    a = pt.angle * PI /180
    pt.radius = pt.radius + (5) * sin((pt.angle) * 2 * PI/30)
    pt.x = SCREEN_W_MID + cos(a) * pt.radius
    pt.y = SCREEN_H_MID + sin(a) * pt.radius
end sub
y smiley is 24 bit.
[Image: anya2.jpg]

Genso's Junkyard:
http://rel.betterwebber.com/
Reply
#2
I can easily agree that... hmm... no... actually -

1. code can be easily modularized
Agreed. But why would you want to split up code that deals with the handling of one type of objects?
2. object AI can be easily debugged because it resides in a single sub/funk
Maybe later on when we get more advanced debugging tools, but for now, what's the difference?
3. less cluttered code
I tend to agree. But it's a matter of opinion.
4. object behavior can be easily modified or changed
Yes. But I don't see how it's easier than changing code in a select case statement.
5. code is not as error prone as a buch of select cases
In what way?

Sorry to be mean here, but a bunch of claimings makes noone wiser - let's hear the arguments! Wink
Reply
#3
Rel you forgot speed!

Joakim -imagine you have like 100 different objects?
you'd have select case with 100 options... This way although you have 100 sub's it's still a LOT cleaner to me... dunno about you thoo. Also adding new object (if main system is well done) you only need to define new type and write a handling sub, everything else is done by object system. I'm too lazy atm -but if you want to see on sutch object system in work, download marzec's fbirc that uses my gui made in FB. It uses sutch object system.
url]http://fbide.sourceforge.net/[/url]
Reply
#4
Well it's not that I disagree really. Although I do use a select case solution at the moment, I am considering the function pointer approach. It's just that I wanted to see some debate over all those claims :lol:

To extend the concept, I'd have an extra type for the class of the object (until oop features like inheritance hits FB), like this:

[syntax="freebasic"]
type CMissileClass
Sprite as CSprite ptr
Damage as double
Radius as double
Mover as sub (byref this as CMissile)
end type

type CMissile
Kind as CMissileClass ptr
X as double
Y as double
XVel as double
YVel as double
end type
[/syntax]

As you can often take a bunch of different objects of the same type and find some values that they have in common.
Reply
#5
Quote:1. code can be easily modularized
Agreed. But why would you want to split up code that deals with the handling of one type of objects?
Would you rather have a humoungous 20,000 select cases for 20,000 behaviors?

Quote:2. object AI can be easily debugged because it resides in a single sub/funk
Maybe later on when we get more advanced debugging tools, but for now, what's the difference?
Huh? What does "debugging tools" have to do with this? The demo was to show how to use function pointers in handling object behaviors, not to release the next FB RPG.

Quote:3. less cluttered code
I tend to agree. But it's a matter of opinion.
Yeah, 20,000 select cases is not cluttered. ;*)

Quote:4. object behavior can be easily modified or changed
Yes. But I don't see how it's easier than changing code in a select case statement.
Okay, let's see we have a ship on a shoot em up moving on sine waves and you suddenly want not to use that object behavior. In select cases you'd do this:
Now let's assume that we have more than a hundred behavior and 56 is the index/tag for sinemove. Original code would look like this:
Code:
select case OBJECT_ACTION
    case    const 0
        dozero        
    case    const 1
        do_one
    case     const 2
        do_blah
    :
    :
    case const 56        'sinemove
        do_sine
    case const 57        
        do_duh
    case else
     end select

Removing the select case for 56 would be prone to errors, and it's just plain messy. You also loose one Object action constant unless you replace it with another behavior.

Code:
select case OBJECT_ACTION
    case    const 0
        dozero        
    case    const 1
        do_one
    case     const 2
        do_blah
    :
    :            '56 is gone unless you replace it with another behavior
    case const 57        
        do_duh
    case else
     end select

Don't tell me you'd re index your constants by hand, because it's going to be reaaaaaly boring.

With Sub and function pointers, I could put the behaviors in one BI file and remove any sub/unction I want w/o the need of reindex. All I have to do is to assign another address to the behavior. Now tell me it's not as easy as sex. :*)

Quote:5. code is not as error prone as a buch of select cases
In what way?

Read #4.


Function pointers have never been depreciated in ANSI/ISO C/C++ because the standards committee knew too well that it's a very useful tool. C++ has been the standard in game coding because of one thing: Classes. And I have shown a way to emulate a little of classes' behavior in FB. As v1c would like to say, "you don't like it, don't use it."


Why would you use a stone to put nails when there is a hammer?
y smiley is 24 bit.
[Image: anya2.jpg]

Genso's Junkyard:
http://rel.betterwebber.com/
Reply
#6
The stone feels more natural to me Wink

Well first things first, I havn't written a whole lot of games with 20,000 different monsters/behaviours in it. Usually if I have like 10 different monsters, I'm pretty proud heh. maybe when it gets crazy I'd top 50 different behaviours, but that would be in extreme cases (if I wanted that much detailed difference, I'd use a scripting language instead).

The problem with indexes you're talking about was first solved with const, then with enum. My code would look more like this:

[syntax="freebasic"]
enum CBehaviours
BE_Circle
BE_Loop
BE_Whatever
end enum
...
select case as const Object.Behaviour
case BE_Circle
...
case BE_Loop
...
case BE_Whatever
...
end select
[/syntax]
Removing the BE_Loop from the enumeration would make the compiler warn me about everything that uses BE_Loop, thus be no more error prone than a function.

Again, I dunno how it'd be easier to debug a function than a case. With debugging tools, I imagine you could trace a crash to a function, that's all.

I'm still not against the idea, but I don't think the case approach is THAT bad...

Whenever class support with inheritance arrives, I'll be using that for sure. As long as the language allows me to use procedural code whenever I feel like it Smile
Reply
#7
Well, I am speaking from experience I got making FJ. ;*)
y smiley is 24 bit.
[Image: anya2.jpg]

Genso's Junkyard:
http://rel.betterwebber.com/
Reply
#8
Debug is bug hunting. The only thing I've used is... a text editor and my eyes, plus lots of prints or a debug file where I write convenient messages to.

Using Rel's approach is way easier as the code is more structured.

Structured code makes debugging easier. I find it easier to spot the bug when the behaviours are described in a separate BAS file each one in its own function rather than having to scan a 50 screens long SELECT case thingo.

Again this is a matter of coding taste. I can't stand 1,000-lines-long Mr-do-it-all structures. You are supposed to split your program as much as you can. More than 30 years of coding experience worlwide assure that: structured code is easier to deal with. That's why M$ got rid of file numbers and added structural constructs to BASIC in the mid 80s.
SCUMM (the band) on Myspace!
ComputerEmuzone Games Studio
underBASIC, homegrown musicians
[img]http://www.ojodepez-fanzine.net/almacen/yoghourtslover.png[/i
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)