Getting into Enums and custom functions

Started by Glenjamin, Thu 01/11/2018 17:29:48

Previous topic - Next topic

Glenjamin

Hey all.

In the game I'm working on, the character has multiple outfits they can equip at any time. However, there are more animations than just the normal view and speech view, such as a view for picking up items.

This would get very messy in the local and global scripts.

I currently have a variable for the outfits(Skins) called "pskin". It's an int, and the number indicates which outfit the player is wearing.

If my understanding of enums is correct(Which it probably isn't), I could make a set of enums, each representing an animation such as "egrab" and "esleep".

Combined with a custom function, I could end up with code like this:

Code: ags

function odoor_AnyClick(){

player.specialanimation(eopendoor, delay, repeat style, blocking, direction);
//Functionally identical to ".animate" only the enum means i don't have to set the views every time
}


I don't know if this would work, no less where to start on how to build it. Any help is greatly appreciated.

Crimson Wizard

#1
Well, your understanding is correct. Enum is simply an integer value that you have given a name, and as such it does two things:

- makes it easier to remember what it means;
- helps you with autocomplete: if a function parameter or variable is of enum type, AGS will show a list of possible values as you type it, just like it does with builtin types.

Enum is created like this:
Code: ags

enum CharacterAnimation
{
   eAnimOpenDoor,
   eAnimCloseDoor,
   eAnimJumpAround
}


Then you may have a variable or function argument of this type:
Code: ags

CharacterAnimation anim;
anim = eAnimJumpAround;

Code: ags

import void DoSomething(CharacterAnimation playAnim, int x, int y);

DoSomething(eAnimOpenDoor, 110, 85);


If your question also about extender functions, that may be run like "character.DoSomething", these are created rather simply too, the only caveat is to remember the slightly weird syntax of the first parameter, which determines which object you are refering to in the function using "this" keyword:
Code: ags

import void ExtraAction(this Character*, other params);

void ExtraAction(this Character*, other params)
{
    this.Walk(100, 100);
    ...
}


In your case the function may be something like:
Code: ags

void SpecialAnimation(this Character*, CharacterAnimation anim, int delay, optional RepeatStyle repeat = eOnce, optional BlockingStyle block = eBlock, optional Direction dir = eForward)
{
    int view, loop;
    if (anim == eAnimOpenDoor)
    {
        view = 10;
        loop = 0;
    }
    else if (anim == eAnimOpenDoor)
    {
        view = 10;
        loop = 1;
    }
//    else ...
//    and so on
    this.LockView(view);
    this.Animate(loop, repeat, block, dir);
}

Snarky

Quote from: Glenjamin on Thu 01/11/2018 17:29:48I currently have a variable for the outfits(Skins) called "pskin". It's an int, and the number indicates which outfit the player is wearing.

This sounds like somewhere you might want to use enums, depending on how the skins vary, how many there are and how they're set. If it's useful to identify the outfits by name in the code, you probably should use an enum instead of an int, so you could write code like pskin = eSkinFancyOutfit; or what have you.

Quote from: Glenjamin on Thu 01/11/2018 17:29:48
If my understanding of enums is correct(Which it probably isn't), I could make a set of enums, each representing an animation such as "egrab" and "esleep".

Combined with a custom function, I could end up with code like this:

Code: ags

function odoor_AnyClick(){

player.specialanimation(eopendoor, delay, repeat style, blocking, direction);
//Functionally identical to ".animate" only the enum means i don't have to set the views every time
}

Mmyeah, you could, but rather than one function that takes a parameter to specify which action it is, wouldn't it be easier to just have one function for each action?

Quote from: Glenjamin on Thu 01/11/2018 17:29:48
In the game I'm working on, the character has multiple outfits they can equip at any time. However, there are more animations than just the normal view and speech view, such as a view for picking up items.

This would get very messy in the local and global scripts.

So what is the issue? Is it the logic to decide which view/loop you should play depending on the outfit and action? How do you have them set up in the view editor? CW's code will work, but if you have it set up consistently, there should be a way to calculate it directly without having to special-case it for every possible value using if-clauses.

Glenjamin

Thanks for the help so far.

QuoteSo what is the issue? Is it the logic to decide which view/loop you should play depending on the outfit and action? How do you have them set up in the view editor? CW's code will work, but if you have it set up consistently, there should be a way to calculate it directly without having to special-case it for every possible value using if-clauses.

Currently I have it set up so each skin has two or more views. The normal view, the speech view, and subsequent views for each generic animation. (Taking damage, picking up items,idle)

Each generic view has an animation for the four cardinal directions. (Otherwise i would've made one giant view with every animation pertaining to that skin)

My goal is to be able to declare:

The current skin is: "Skin". So instead of going through the view list every time I can just put "egrab" and the engine uses animations for that skin rather than a sea of "else if" for every single function



Khris

#4
My suggestion would be:

Code: ags
// header
enum eHeight {
  eUp, eMiddle, eGround  // eUp = 1, eMiddle = 2, eGround = 3
};


And:
Code: ags
void SpecialAnimation(Character* c, int loop) {
  // calculate view based on current NormalView 
  int view = this.NormalView + 2;  // example formula
  c.LockView(view);
  c.Animate(loop, 5, eOnce); // eForward, eBlock are defaults
  c.UnlockView();
}

void Reach(this Character*, eHeight height) {
  SpecialAnimation(this, height - 1); // loop 0: reach up animation, loop 1: reach forward, etc
}

void GetHit(this Character*) {
  SpecialAnimation(this, 3);
}


Once you have that set up, all you need is

Code: ags
  player.Reach(eUp);
  // or
  player.GetHit();


If you need an animation to run slightly faster or slower than the default of 5, set frame delays in the view editor

Glenjamin

This is a little intimidating, but mostly because I'm a massive coward.

I'll give each method a shot, but I think Snarky's idea to make a function for each animation would be the cleanest.

Quote
Code: ags
 // calculate view based on current NormalView 
  int view = this.NormalView + 2;  // example formula

I didn't implement the views mathematically. My brain immediately jumps to an "If/else" script for every outfit. There shouldn't be more than 10 so I don't think it would be THAT bad. I still think I'm missing something.

I'm gonna fiddle with it.

Khris

Reach and GetHit are just convenience functions, shortcuts for the actual SpecialAnimation() call. All they do is play a specific loop of the view. The height enum justs masks the fact that you're calling player.Reach(0); or player.Reach(1); or player.Reach(2);

QuoteMy brain immediately jumps to an "If/else" script for every outfit.
That's what people do, but there's often better ways, especially if there's a lot of uniformity to the underlying data. A good rule of thumb is to use as few if/else as humanly possible.

SMF spam blocked by CleanTalk