Fully-rotating, top-down view, animated sprite.

Started by cantgetno197, Mon 04/05/2015 22:56:12

Previous topic - Next topic

cantgetno197

Hi, I've been working on a game for a while which has a quasi-turn-based battle system with real-time components.  The system uses a top-down view and requires the character to look in one direction and move in another.  They can also rotate by any angle.  Basically, top-down "WASD" movement with the ability to look in any direction, except moves are planned and not done in real-time.  I've tried a number of ways of going about achieving this and have read every post on the topic I can find but nothing seems to work.  I don't want to make 6 billion sprites, one for each possible orientation and walking direction.  My sprites are small and I would think can be rotated on the fly without slow-down.  Therefore my ideal solution would be to simply have FOUR views: looking forward - walking forward, looking forward - walking right, looking forward - walking left and looking forward - backward. Then I would take all the frames in the appropriate animation and simply rotate them either on the fly or pre-make an array of some sort in dynamic memory.

However,  I've tried to accomplish something like this with DynamicSprites but it doesn't seem to work as I don't think you can change the sprites in character views in real-time.  Does anyone have any advice?

Khris

You _can_ change view sprites. The basic idea is:
Code: ags
  ViewFrame *vf = Game.GetViewFrame(view, loop, frame);
  vf.Graphic = left_forward.Graphic;

Where left_forward is a global DynamicSprite.

Here's an example game with a freely rotating "head" following the mouse, on a WASD controlled base (AGS 3.2.1 source): https://www.dropbox.com/s/f6xb7am3dr85f6b/Mech.zip?dl=1

cantgetno197

Thank you very much for your reply and your attached code pretty much does exactly what I want. The key thing I think I was doing wrong was that the DynamicSprite MUST be global to the script.  I don't fully understand why that must be the case but if I do something like

Code: ags

DynamicSprite *guy=DynamicSprite.Create(40, 40);
          DynamicSprite *tt= DynamicSprite.CreateFromExistingSprite(16);
          tt.Rotate(180);
          DrawingSurface *test=guy.GetDrawingSurface();
          test.DrawImage(0, 0, tt.Graphic);
          test.Release();
          tt.Delete();
          ViewFrame *vf = Game.GetViewFrame(player.NormalView, 0, 0); // down, standing
          vf.Graphic = guy.Graphic;
          


It just cycles through my sprites but if I declare "guy" at the top of the script I get it to work.  Regardless, thank you so much for your help.

Khris

Anything declared inside a function is local to it and gets deleted as soon as the function finishes.
As you found out, simply moving the declaration outside the function will fix it.

cantgetno197

But I guess I don't understand WHAT is passing out of scope.  In my mind, in the previous code I posted, vf.Graphic is a pointer to some actual sprite in global memory that the engine is using to display the character. and

Code: ags

vf.Graphic=guy.Graphic


is a pass by VALUE that should set the values in the chosen, global, sprite in vf with the value that is currently in guy.Graphic... then I get rid of guy.Graphic.  The only reason I can think of why this doesn't work is if this equality is actually changing the POINTER, so that vf.Graphic now points to guy.Graphic which is locally defined. But why would that be the case?  Why can't I over-write the global image AGS has that is the image it uses to display the character?  Why do I need to point it to some new dynamically created image that will scope out at some point?

Khris

.Graphic is an int, it stores the global sprite slot. It kind of is a pointer; the line does not duplicate a bitmap in memory.

Say your game has 50 sprites, numbered 1 to 50 (and the 0 slot).
Creating a new DynamicSprite will assign it slot 51.
Now when you call vf.Graphic = guy.Graphic; you're simply setting the viewframe's sprite slot to 51.
As soon as the (local) sprite gets deleted, the viewframe's .Graphic now stores a slot that no longer exists.

cantgetno197

I see.  Thank you so much for you help,  I was wondering if I could bother you with one quick last question. Can I write directly to a sprite in global memory?  Like let's say my game has only one character who has 4 movement animations, each with 5 frames.  Can I have slots 1-20 (4*5) as each one and then the game only ever renders the character using sprites 21-25 in a loop.  I can then grab, say sprites, 6-10, rotate it in local memory, and then write it to 21-25 to have the character turn.  My memory usage is fixed and the screen is only ever displaying the same 5 frames.  Is this something that can be done?

Khris

You can change DynamicSprites as much as you like, but your game has to create them first.
For instance like this:
Code: ags
DynamicSprite *left;
...
  left = DynamicSprite.CreateFromExistingSprite(12);
  left.Flip(eFlipLeftToRight);


You always have to assign their sprite slots to a View's frames manually though.

cantgetno197

Thanks again for the response.  Unless I'm really stupid it really doesn't seem to be that simple.  If I have the following code:

Code: ags

    player.ChangeView(PC_MISS);
    cEnemy_1.ChangeView(PC_MISS);

    ViewFrame *vf0;
    DynamicSprite *temp_ds;
    DrawingSurface *temp_drawing;

    int char_num=0;
    while(char_num<num_characters){
        Character *the_char=characters[char_num];
        i=0;
        while(i<Game.GetFrameCountForLoop(the_char.View, 0)){
          player_move_frames[char_frame_starts[char_num]+i]=DynamicSprite.Create(45, 45);
          temp_ds= DynamicSprite.CreateFromExistingSprite(char_animation_starts[char_num]+i);
          temp_drawing=player_move_frames[char_frame_starts[char_num]+i].GetDrawingSurface();
          temp_drawing.DrawImage(0, 0, temp_ds.Graphic);
          temp_drawing.Release();
          temp_ds.Delete();
          ViewFrame *vf = Game.GetViewFrame(the_char.View, 0, i);

          Display("Setting %s new frame %i from %i to %i",the_char.Name, char_frame_starts[char_num]+i, vf.Graphic, player_move_frames[char_frame_starts[char_num]+i].Graphic);

          vf.Graphic = player_move_frames[char_frame_starts[char_num]+i].Graphic;
          i++;
         }
         char_num++;
   }



Basically I have two characters, player and cEnemy_1 and initially they both have the same view (PC_MISS) however I want to make two copies taking the spaces 0-4 and 5-9 of player_move_frames which I then set to their viewframes and I then if I want to turn/rotate player (cEnemy_1) I change frames 0-4 (5-9) of player_move_frames (which is declared globally).  It doesn't work though because somehow vf.Graphic of cEnemy_1 and player_move_frames[0-4].Graphic (I.e. the dynamically create set of DynamicSprites for player) are the same integer as in they somehow point to the same place.  player_move_frames is declared at run-time.  What is happening?

Khris

First, it looks like what you're currently doing in the while loop (copying the sprite) can be achieved using this:

Code: ags
int f = char_frame_starts[char_num] + i;
int a = char_animation_starts[char_num] + i;

player_move_frames[f] = DynamicSprite.CreateFromExistingSprite(a);
vf.Graphic = player_move_frames[f].Graphic;


Regarding the error, it looks like you're assigning the same view to both characters, which means the code changes the view sprites to one character's sprites, then the other's.

You will need separate views for the characters, or draw them yourself (instead of using AGS views to display them).

cantgetno197

Ah, excellent.  I might draw the views myself. Thank you very much for your help.

ollj

someone made a script and demo game to have GTA1-like topdown-racecars within ags go look at that!

SMF spam blocked by CleanTalk