Timeline Track Objects doing the same function?

Hi, I have a problem when I run my code. I want to have many enemy spiders that move but only the first one moves. If someone could help me that would be appreciated.
Here is some of my Config code:

[Scene]
TrackList          = enemyMakerTrack

[enemyMakerTrack]
1                     = Object.Create spiderObject
Loop                = True

[spiderObject]
Graphic            = spiderGraphic
Scale                = 0.2
Position            = (800, 275, 0)
AnimationSet    = spiderAnimationSet
Body                 = spiderBody

[spiderGraphic]
Texture         = spider_full.png
TextureOrigin   = (0, 0, 0)  
TextureSize     = (0, 0, 0)
Pivot           = center

[spiderAnimationSet]
Texture         = spider_full.png
FrameSize       = (1024, 1024, 0)
Pivot           = center
spiderWalkRight  = 5
spiderWalkLeft   = 5

StartAnim       = spiderWalkLeft

spiderWalkLeft-> = spiderWalkLeft # .spiderWalkRight
spiderWalkRight-> = spiderWalkRight # .spiderWalkLeft

[spiderWalkLeft]
KeyDuration     = 0.1
TextureOrigin   = (0, 1024, 0)

[spiderWalkRight]
KeyDuration     = 0.1
TextureOrigin   = (0, 0, 0)

[spiderBody]
Dynamic         = true
PartList        = spiderBodyPart
LinearDamping   = 5

[spiderBodyPart]
Type            = box
Solid           = true
SelfFlags       = enemy
CheckMask       = platforms # bullet
BottomRight     = (225, 100, 0)
TopLeft         = (-200, -175, 0)

and here is some of my c++ code:

#include "orx.h"

orxOBJECT* spider;

void spiderMovement()
{
    orxVECTOR spiderLeftSpeed = { -3, 0, 0 };
    orxVECTOR spiderRightSpeed = { 3, 0, 0 };

    if (dead == 0)
    {
        orxObject_ApplyImpulse(spider, &spiderLeftSpeed, orxNULL);
        orxObject_SetTargetAnim(spider, "spiderWalkLeft");
    }
}

orxSTATUS orxFASTCALL Init()
{
     orxObject_CreateFromConfig("Scene");

     spider = orxObject_CreateFromConfig("spiderObject");
}

orxSTATUS orxFASTCALL Run()
{
      spiderMovement();
}

This is not the whole code it is just things that are regarding to the problem.
I can post the whole code if you like.

Tagged:

Comments

  • edited December 2019

    @Super_Michael_05 said:
    Hi, I have a problem when I run my code. I want to have many enemy spiders that move but only the first one moves. If someone could help me that would be appreciated.

    Hi @Super_Michael_05, let's see what you have!

    [...]

    [Scene]
    TrackList          = enemyMakerTrack
    
    [enemyMakerTrack]
    1                     = Object.Create spiderObject
    Loop                = True
    

    Here you have a Scene object that you use to create a new spider every second. Is that what you want?
    If so, you can check spawners to that effect.

    [...]

    [spiderAnimationSet]
    Texture         = spider_full.png
    FrameSize       = (1024, 1024, 0)
    Pivot           = center
    spiderWalkRight  = 5
    spiderWalkLeft   = 5
    
    StartAnim       = spiderWalkLeft
    
    spiderWalkLeft-> = spiderWalkLeft # .spiderWalkRight
    spiderWalkRight-> = spiderWalkRight # .spiderWalkLeft
    
    [spiderWalkLeft]
    KeyDuration     = 0.1
    TextureOrigin   = (0, 1024, 0)
    
    [spiderWalkRight]
    KeyDuration     = 0.1
    TextureOrigin   = (0, 0, 0)
    

    Here I'd recommend having generic animations/set instead, this way you can re-use it for other enemies and use the same animation code to control them. Something like:

    [enemyAnimationSet]
    Pivot         = center
    KeyDuration   = 0.1
    FrameSize     = (1024, 1024, 0)
    WalkRight     = 5
    WalkLeft      = 5
    
    StartAnim     = WalkLeft
    
    WalkLeft->    = WalkLeft # .WalkRight
    WalkRight->   = WalkRight # .WalkLeft
    
    [WalkLeft]
    TextureOrigin = (0, 1024, 0)
    
    [spiderAnimationSet@enemyAnimationSet]
    Texture       = spider_full.png
    

    Of course if the size/number of frames per animation is different for every enemy, you can override them in each specific animation set by using the Prefix feature.

    Also, are you sure your FrameSize is correct? It seems much too large.

    [...]

    #include "orx.h"
    
    orxOBJECT* spider;
    
    void spiderMovement()
    {
        orxVECTOR spiderLeftSpeed = { -3, 0, 0 };
        orxVECTOR spiderRightSpeed = { 3, 0, 0 };
    
        if (dead == 0)
        {
            orxObject_ApplyImpulse(spider, &spiderLeftSpeed, orxNULL);
            orxObject_SetTargetAnim(spider, "spiderWalkLeft");
        }
    }
    
    orxSTATUS orxFASTCALL Init()
    {
         orxObject_CreateFromConfig("Scene");
    
         spider = orxObject_CreateFromConfig("spiderObject");
    }
    
    orxSTATUS orxFASTCALL Run()
    {
          spiderMovement();
    }
    

    Here you create yet another spider, this one you control it by code in spiderMovement. The other spiders that are created through the track on Scene are not controlled by this code, so they won't be moving.

    Do you want to control all the spiders as one or all of them independently?

    If you want to control them as one, you could have them child of a single parent object and control them through that object instead. If you want to control them independently, you'll need to loop over them.
    One of the easiest way to do that is either to use Scroll or to add a config property such as IsEnemy = true and test it in a loop, something like:

    void enemyMovement()
    {
      for(orxOBJECT *enemy = orxOBJECT(orxStructure_GetFirst(orxSTRUCTURE_ID_OBJECT));
           enemy;
           enemy = orxOBJECT(orxStructure_GetNext(enemy)))
      {
        orxConfig_PushSection(orxObject_GetName(enemy));
        if(orxConfig_GetBool("IsEnemy"))
        {
          // Here the animation code is generic and will work with any animation set deriving from `enemyAnimationSet`, not just spiders
          orxObject_SetTargetAnim(enemy, "WalkLeft");
          // Do everything you want here
        }
        orxConfig_PopSection();
      }
    }
    

    I also recommend using orxObject_SetSpeed instead of applying an impulse, it'll be much easier to control.
    Please note that as I wrote the code directly in the forum, there might be typos.

  • Thank you! I will get back to you when I have updated my code.

  • It works but I can't get the orxObject_SetSpeed to work.
    Here is where I added it:

    void enemyMovement()
    {
        orxVECTOR* LeftSpeed = (-3, 0, 0);
        orxVECTOR* RightSpeed = (3, 0, 0);
    
        for (orxOBJECT* enemy = orxOBJECT(orxStructure_GetFirst(orxSTRUCTURE_ID_OBJECT));
            enemy;
            enemy = orxOBJECT(orxStructure_GetNext(enemy)))
        {
            orxConfig_PushSection(orxObject_GetName(enemy));
            if (orxConfig_GetBool("IsEnemy"))
            {
                orxObject_SetTargetAnim(enemy, "WalkLeft");
    // Here
                orxObject_SetSpeed(enemy, LeftSpeed);
            }
            orxConfig_PopSection();
        }
    }
    

    Thanks!

  • edited December 2019

    I believe your problem here is two-folds:

    1. you should be using local vectors and pass their address, here you are storing vector pointers, not vectors. You'd need something like:
    orxVECTOR speed = {-3, 0, 0};
    orxObject_SetSpeed(enemy, &speed);
    
    1. Speeds are expressed in pixels/second. 3 pixels per second seems like a very very slow speed and probably not what you want.

    Lastly, you can use the config system in order to retrieve the speeds, this way you can tweak them without having to recompile (you can even tweak them on-the-fly during runtime if you're using the resource's WatchList feature).

    Ex:

    [spiderObject]
    LeftSpeed = (-100, 0)
    RightSpeed = (100, 0)
    
    void enemyMovement()
    {
        for (orxOBJECT* enemy = orxOBJECT(orxStructure_GetFirst(orxSTRUCTURE_ID_OBJECT));
            enemy;
            enemy = orxOBJECT(orxStructure_GetNext(enemy)))
        {
            orxConfig_PushSection(orxObject_GetName(enemy));
            if (orxConfig_GetBool("IsEnemy"))
            {
                orxVECTOR speed;
                orxConfig_GetVector("LeftSpeed", &speed);
                orxObject_SetTargetAnim(enemy, "WalkLeft");
                orxObject_SetSpeed(enemy, &speed);
            }
            orxConfig_PopSection();
        }
    }
    
  • My pleasure!

  • For some reason it still doesn't work it plays the animation as it should but it doesn't move.

  • Did you set a bigger value on it? If you want you can send me your whole project and I can have a look at it.

  • edited January 2020

    Can I send you a link to my repository in Github then you could it download on to your computer?

  • Perfect, I'll have a look this afternoon and will let you know what I've found.
  • edited January 2020

    I do not see any code to move the spiders in your project at the moment. Did you remove it?

  • Ah sorry my bad I fixed it and pushed it but it still doesn't work.

  • I'm doing some refactoring, hope you don't mind, mostly the animation sets and the size of the textures that were ~1000 times bigger than they should have been, and it made the debug version very slow on my computer. :)

  • edited January 2020

    As I started working on the previous version, I didn't check your enemy code, however I added a functional version on my side.
    I did some changes to your project, feel free to revert what you don't like:

    • shrunk all textures by 32x32 -> much better performance and video memory use: 480kb vs 350mb
    • refactored movement/shooting/pickup code with more generic animation sets + gun handling (new guns can be added in config only, with different stats and animations, without the need to change the code)
    • code is about 1/3 shorter, but it could be reduced even more

    Don't hesitate if you have any questions about some of the changes I made. Hope it'll help!

    The PR can be found at https://github.com/Super-Michael-05/Space-Shooter-Current-Project/pull/1

  • Ah, you'll need the latest version of orx from its github repo, btw, as I fixed a small animation set inheritance bug, thanks to your project. :)

  • I am glad my project helped with the development of ORX and thank you a lot!

  • I implemented a Health system but it doesn't work and when I check the console and add Config.GetValue Enemy Health it doesn't give a value. If you can help me I have my code here: https://github.com/Super-Michael-05/Space-Shooter-Current-Project

  • edited January 2020

    First of all, I'd recommend removing all runtime files (the logs, the build files, the debug files, etc...) from your repository, it makes a lot of noise when looking at diffs and will make the size of your repo grow very fast.

    If we look at your Enemy section, there's no Health property in it:

    [Enemy]
    IsEnemy         = true
    AnimList        = WalkLeft # WalkRight
    TrackList       = RandomAnimTrack
    AnimList        = WalkLeft # WalkRight
    ChildList       = DebugObject
    

    That's why Config.GetValue Enemy Health returns an empty value. Whereas you have:

    [spiderObject@Enemy]
    HorizontalSpeed = 19
    Health          = 50
    Graphic         = spiderGraphic
    Position        = (0, 0) ~ (250, 0)
    AnimationSet    = spiderAnimationSet
    Body            = enemyBody
    

    So, Config.GetValue spiderObject Health would return 50.

    Speaking of which, when doing a variation of an enemy type, I'd recommend inheriting from the base type of that enemy instead of Enemy directly and duplicating all the properties that are the same.

    Right now you have:

    [spiderObject@Enemy]
    HorizontalSpeed = 19
    Health          = 50
    Graphic         = spiderGraphic
    Position        = (0, 0) ~ (250, 0)
    AnimationSet    = spiderAnimationSet
    Body            = enemyBody
    
    [fastSpiderObject@Enemy]
    HorizontalSpeed = 39
    Health          = 50
    Graphic         = fastSpiderGraphic
    Position        = (0, 0) ~ (250, 0)
    AnimationSet    = fastSpiderAnimationSet
    Body            = enemyBody
    

    I'd suggest something like:

    [spiderObject@Enemy]
    HorizontalSpeed = 19
    Health          = 50
    Graphic         = spiderGraphic
    Position        = (0, 0) ~ (250, 0)
    AnimationSet    = spiderAnimationSet
    Body            = enemyBody
    
    [fastSpiderObject@spiderObject]
    HorizontalSpeed = 39
    Graphic         = fastSpiderGraphic
    AnimationSet    = fastSpiderAnimationSet
    

    This way the changes done to the base spider will also affect its fast version.

    Now regarding your code, I found:

    orxSTATUS orxFASTCALL PhysicsEventHandler(const orxEVENT* _pstEvent)
    {
        if (_pstEvent->eID == orxPHYSICS_EVENT_CONTACT_ADD) {
            // We store them as two pairs (sender/recipient then recipient/sender), so as to not duplicate the logic code
            struct
            {
                orxOBJECT *Self, *Other;
            } Colliders[2] = {
                {orxOBJECT(_pstEvent->hSender), orxOBJECT(_pstEvent->hRecipient)},
                {orxOBJECT(_pstEvent->hRecipient), orxOBJECT(_pstEvent->hSender)}
            };
    
            orxOBJECT* enemy = orxObject_CreateFromConfig("Enemy");
            orxOBJECT* gun = orxObject_CreateFromConfig("Gun");
    
            orxConfig_PushSection(orxObject_GetName(enemy));
            orxFLOAT enemyHealth = orxConfig_GetS32("Health");
    
            orxConfig_PushSection(orxObject_GetName(gun));
            orxFLOAT gunDamage = orxConfig_GetS32("Damage");
    
            // Iterating over the two pairs
            for(orxU32 i = 0; i < orxARRAY_GET_ITEM_COUNT(Colliders); i++)
            {
                orxConfig_PushSection(orxObject_GetName(Colliders[i].Other));
    
                // Platform?
                if(orxConfig_GetBool("IsPlatform"))
                {
                    canJump = true;
                }
                // Gun pickup?
                else if(orxConfig_GetBool("IsGun"))
                {
                    PickUp(Colliders[i].Other);
                }
                // Bullet?
                else if(orxConfig_GetBool("IsBullet"))
                {
                    // Die
                    //orxObject_SetTargetAnim(Colliders[i].Self, "Dead");
    
                    enemyHealth = enemyHealth - gunDamage;
    
                    // Remove bullet
                    orxObject_SetLifeTime(Colliders[i].Other, orxFLOAT_0);
                }
                else if (enemyHealth == 0)
                {
                    orxObject_SetTargetAnim(Colliders[i].Self, "Dead");
                }
    
                orxConfig_PopSection();
            }
        }
        return orxSTATUS_SUCCESS;
    }
    

    The part

            orxOBJECT* enemy = orxObject_CreateFromConfig("Enemy");
            orxOBJECT* gun = orxObject_CreateFromConfig("Gun");
    
            orxConfig_PushSection(orxObject_GetName(enemy));
            orxFLOAT enemyHealth = orxConfig_GetS32("Health");
    
            orxConfig_PushSection(orxObject_GetName(gun));
            orxFLOAT gunDamage = orxConfig_GetS32("Damage");
    

    doesn't make much sense to me. That means every time a collision occurs, you're going to create two new objects, Enemy and Gun, but those don't have any purpose and will linger on forever.

    You do not need to have objects to retrieve config values. However if you want to retrieve values for an existing object, then you can use the object's name as the config section to push.

    You do not have access to the gun in this event handler, only the character that got hit and the bullet. As such, I'd recommend setting the Damage value on the bullet instead of the gun (which works for you as you have different bullets for your different guns). You can even store it on the gun and inherit it at the bullet's level as well.

    At the moment, you do not store the Health on every character. You need to either attach your own structure to the characters that have health when they're created, so as to be able to track them over time, or simply use Scroll (https://github.com/orx/scroll) instead, which will make this much easier as you'll then have your own C++ classes for the objects that need runtime values, like health.

    Here's a part of what you need, assuming the above (there are ways to be more generic/automated, but let's focus on a simpler, more manual, approach, for now):

    [pistolBulletObject@Bullet]
    Graphic         = bulletGraphic
    LifeTime        = 0.7
    Body            = bulletBody
    Damage          = @pistolObject
    
    [rifleBulletObject@Bullet]
    Graphic         = bulletGraphic
    LifeTime        = 1.0
    Body            = bulletBody
    Damage          = @rifleObject
    
                // Bullet?
                else if(orxConfig_GetBool("IsBullet"))
                {
                    // Here Other is the bullet and Self is the character that got hit
                    // As we've already pushed Other's (ie. the bullet's) config section a few lines above
                    // Let's retrieve its damage directly
                    orxS32 damage = orxConfig_GetS32("Damage");
    
                    // Here we would retrieve the character's health, that was initialized when created
                    // And if health <= 0, then we'd die with:
                    // orxObject_SetTargetAnim(Colliders[i].Self, "Dead");
                    // As it's a much bigger change as stated above, I won't display it here.
                    // I recommend using Scroll (https://github.com/orx/scroll) and checking the tutorials on the wiki
                    // Such as:
                    // https://wiki.orx-project.org/en/tutorials/community/acksys/scroll0
                    // And
                    // https://wiki.orx-project.org/en/tutorials/scrollobject_to_scrollobject_communication
                }
    
Sign In or Register to comment.