Jump to content

How do you handle tasks


Rick
 Share

Recommended Posts

They would be different class entirely since the functionality is different. It's of the thought process of each different set of functionality at it's lowest form gets it's own action class to do that functionality. You could create a hierarchy at this level if you wanted though to share functionality. Maybe our transition to and from we want the same, but just the update we need to do something differently. You can make a base class so the enter/leave transition methods are called from the base while the update is specific to the child. All these actions have their own parameters as well, so you can get some custom stuff that way if needed. Notice the data contract idea that is required for an action. You can branch this way by querying the object information that the action expects the object to have to do some branching if required, but I think making a new action class would be better myself for code separation and maintainability.

 

Can you give a pseudo example of your MoveTo? I think the actions idea might just work within FSM.

Link to comment
Share on other sites

We're doing a lot of this kind of thing for Darkness Awaits, and both Chris and I take a simple approach. Our AI update functions basically look like this:

 

if state==STATE_ANGRY
if self.targetenemy~=nil then
if self.entity:DistanceToEntity(self.targetenemy)>5
local d = (self.GetPosition() - self.targetenemy:GetPosition())
targetposition = self.GetPosition(true) + d.Normalize() * maxmovespeed
//something like that
endif
endif
elseif state==STATE_HAPPY
//do some code
elseif state==STATE_HUNGRY
///do some code
endif

 

Yes, it can become spaghetti, but that's just life. You can either design code around a wonderful beautiful abstract system, in which case you will have trouble making it do what you actually want it to do, or you can design code around specific behavior you want, in which case the code will be somewhat messy. In my experience, trying to abstract it out beyond that just leads to a lot of confusion and you always find things you didn't anticipate, and then you have to go back and write an even more abstract system.

 

Here's my AI from "Metal". It's pure Spaghetti, but it does what it's supposed to do. All the AI has to navigate the level are hand-placed linked nodes with just one or two per room:

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Guest Red Ocktober

what i've gathered from reading through the discussion is that there is a lot of bandying about of terms and concepts... things like classes, behaviours trees, tasks, etc... they all sound cool... lets delve a lil closer at what (i think) this is all about... bear with me as i try to sound like i know what i'm babbling about...

 

 

i'm a devotee of the self contained class concept... for everything...

 

once you make it... you can drop it in a virtual world... it'll "live", it'll react to other objects in the world, and it'll die... all with no direct behavioral modifying intervention...

 

but this only works if the decendant classes have been well thought out... for, in the virtual world, as in the real worked... no man is an island... nothing is really self contained to the point where it is not aware of other things that exist in the world... at some point we are all connected...

 

the great grandfather of the player's "avatar" class as well as the remote ancestor of the fireball class must have a mechanism that would make them aware of one another... farther up the evolutionary tree you can add specific behaviors (player gets burnt, wood hut would start a fire, ammo would explode, etc, etc)

 

once this logic is implemented a down the evolutionary tree, it becomes easier to deal with farther up the tree... easier to add specific behaviors for specific type object that are derived higher up the evolutionary tree...

 

now you can add logic that should've been "instinctive", at various stages of a class' evolution... but this not only will be more work... but will result in inconsistent and sometimes confusing code...

 

external behavioral trees is something i wouldn't waste time with... i see stuff like this as an indication of poor class development... something that'll only add to the confusion of an already overflowing plate of speggetti...

 

 

--Mike

Link to comment
Share on other sites

They would be different class entirely since the functionality is different.

Exactly, but this negates the whole idea of abstraction because now you are creating Actions which are tied to specific Actors. You effectively do the same if you start conditionally branching within the object in order to supply differing behavior. There is really no escaping this, which is why most game designers design behavior for actors. Sure there is some commonality that can be shared but its difficult if not impossible to keep it completely generic!

 

The Action idea would work within a FSM system but its really not necessary because the State equates to an Action. Whatever you need to provide to the Action must inherently come from the State which rather than passing it to an object that subsequently makes the calls might as well make those very calls itself! There is nothing to stop you defining this additional layer if you so wish, there could be some advantages in some circumstances but it's all additional calling overhead for not that much gain. The States are classes which can simply be instantiated whenever you need them just in the same way as your Actions are!

 

I'm a great believer though of implementing whatever works for us as individuals and whatever we feel comfortable with. Josh's suggestions are just as valid. I think systems like FSMs and Behavior Trees enable you to tie in the decision making to the action commands in a way that is more maintainable and easier to follow but every system has its advantages and disadvantages. Non hierarchical FSMs can lead to a lot of repeated logic, Behavior Trees I believe are better in this respect but I have never used them. But regardless of the system you use ... AI decision making and the actions resulting from those decisions are inextricably interconnected and are influenced by the subject they are acting on.

Intel Core i5 2.66 GHz, Asus P7P55D, 8Gb DDR3 RAM, GTX460 1Gb DDR5, Windows 7 (x64), LE Editor, GMax, 3DWS, UU3D Pro, Texture Maker Pro, Shader Map Pro. Development language: C/C++

Link to comment
Share on other sites

I'm a great believer though of implementing whatever works for us as individuals and whatever we feel comfortable with. Josh's suggestions are just as valid

 

sad.png I was hoping we weren't trying to convience each other of anything but instead just having a conversation of design and showing our designs. No right or wrong, just talking about design. Maybe you saw this as me saying mine design was better than yours? If so I'm sorry as I did not mean for that. Simply looking for what's the same what's different. I know these can be hard discussions to have because people often get attached to their systems and fight to the death to defend them and I hope I wasn't coming off that way.

 

 

There is really no escaping this, which is why most game designers design behavior for actors. Sure there is some commonality that can be shared but its difficult if not impossible to keep it completely generic!

 

For sure. For me it's just about separation.

 

The Action idea would work within a FSM system but its really not necessary because the State equates to an Action.

 

What kind of action though? I'm curious to see how you do it for learning purposes because the actions I've seen were all higher level actions that when you break them down were really many lower level actions all done in the 1 state, which can start making the state complex and a bigger class. If you treat it differently I'd love to see example because maybe I haven't seen the right examples (the inet is full of junk :) ). If I've missing something with my understanding I'd like to know about it ya know :)

Link to comment
Share on other sites

Write a little bit of abstraction to give structure but most importantly the code needs to be hack-able and able to handle some good spaghetti I am written many complex web interfaces and it usually comes down to some good spaghetti. Conventions can help to reduce confusion among fellow developers.

Link to comment
Share on other sites

@Josh, Good old skettie code! Sometimes I want to go back to it because it's easier while you are writing.

 

@Red, I do enjoy putting an object into the world and just having it work. It is intersting how the game industry is really putting programmers on making tools and systems more for designers to actually make the game. I can see why they do it that way.

Link to comment
Share on other sites

I agree Rick, there is no right or wrong and I wasn't meaning to imply mine was any more right than yours. As I said originally, if it works then it's fit for purpose, the game player really doesn't care.

 

I think you are right when you describe higher level actions, when you break them down, being really many lower level actions all done in the 1 state. This does lead to complexity and a bigger class but with complexity comes complexity, it's kind of hard to avoid that.

 

Here by means of illustration is an example from my previous FSM system (as I'm using the EKI One Hierarchical FSM system now). This is the moveToCover state which would be triggered by a perceived enemy in whatever state it was currently in. It checks that it is closer to the cover point than the enemy before switching to the moveToCover state, if no cover points are closer it will simply lie down and engage the enemy from its current position. This previous version of my NPC class had functions embedded in it for supporting general AI or for calling the AI manager when requiring support from other systems:

 

void moveToCover::Enter(npc* pNPC)
{
   pNPC->setCurrentFSMState("moveToCover");

   // Stop path finding
   pNPC->stopCurrentActivity();

   // Get the nearest cover point and move to it
   pNPC->moveToPosition(pNPC->getNearestCoverPointPosition(), 0.9, RifleRun, 160, 1);
}

void moveToCover::Execute(npc* pNPC)
{ 
   int randomTime;
   TVec3 rot1;
   TVec3 rot2;

   // Has the NPC reached the cover point
   if(pNPC->isAtPosition(pNPC->getNearestCoverPointPosition(),0.8))
   {
       // Set the atCoverPosition flag
       pNPC->setIsAtCoverPoint(true);

       pNPC->rotateToVector(pNPC->getNearestCoverPointRotation());

       // If its a partial cover point
       if(!pNPC->getCoverPointIsFull())
       {
           // Set the NPC to crouch
           pNPC->addAnimationToQueue(RifleCrouch, 0.0, yes, no, 0);
       }

       // If NPC Status Type is set to Attack
       if(pNPC->getCurrentAlertState() == alert)
       {
           // If the cover point is of type full
           if(pNPC->getCoverPointIsFull())
           {
               // Wait for a random period of time and then trigger moveToCoverFirePointPos state
               // Store the state to be triggered after wait
               pNPC->setNextState("moveToCoverFirePointPos");

               // Create a random time between 1000 and 3000 mS
               randomTime = 2000 + rand()%3000;

               // Store the wait time
               pNPC->setWaitTime(randomTime);

               // Change state
               pNPC->GetFSM()->ChangeState(waitThenTriggerState::Instance());
           }
           // The cover point is partial
           else
           {
               // Wait for a random period of time then trigger standAndFire state

               // Store the state to be triggered after wait
               pNPC->setNextState("standAndFire");

               // Create a random time between 1000 and 3000 mS
               randomTime = 2000 + rand()%3000;

               // Store the wait time
               pNPC->setWaitTime(randomTime);

               // Change state
               pNPC->GetFSM()->ChangeState(waitThenTriggerState::Instance());      
           }
       }
   }
}

void moveToCover::Exit(npc* pNPC)
{

}

 

I find this pretty simple to keep track of but other people might not. As I say, we all find ways of doing things and systems that work for us. But it is interesting to hear about and discuss other peoples systems.

Intel Core i5 2.66 GHz, Asus P7P55D, 8Gb DDR3 RAM, GTX460 1Gb DDR5, Windows 7 (x64), LE Editor, GMax, 3DWS, UU3D Pro, Texture Maker Pro, Shader Map Pro. Development language: C/C++

Link to comment
Share on other sites

I read the description here of behavior trees. I really do not see why anyone would use this, because you're going to have to buckle down and write the code in the terminal nodes eventually anyways:

http://www.altdevblo...behavior-trees/

 

It seems like it just adds another layer of complexity to something that's already complicated. I mean, it's basically just a bunch of nested 'if' statements.

 

If you had multiple people working together on many different parts of the AI, then it might be a way to break it down so they can't screw each others' code up, but otherwise it just seems like a way to fiddle around and avoid doing the actual code.

 

The FSM stuff sounds enticing because it promises to provide a general-purpose interface for transitions, but I don't buy it. The hardest part is animation blending, and we wrote a very specialize script that handles that. It would not have worked as part of any abstract template.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I read the description here of behavior trees. I really do not see why anyone would use this, because you're going to have to buckle down and write the code in the terminal nodes eventually anyways

That's fine, that's pretty much what I've been advocating in this thread. Do whatever you feel comfortable with and leave other things to other people if they feel comfortable with those.

 

Not everything is obvious at first glance, you had little time for C++ a few years back Josh and now its one of your biggest selling points! Our perception of things change sometimes when we actually start using them, sometimes they don't and it just re-enforces our initial suspicions.

Intel Core i5 2.66 GHz, Asus P7P55D, 8Gb DDR3 RAM, GTX460 1Gb DDR5, Windows 7 (x64), LE Editor, GMax, 3DWS, UU3D Pro, Texture Maker Pro, Shader Map Pro. Development language: C/C++

Link to comment
Share on other sites

Decision Trees are sweet but to make a good one is complex. Their entire purpose is to basically allow non programmers (designers) to make the logic of the game with visual tools and system that the programmers made for the game, and be very flexible. They can easily edit the flow. No doubt the code for all the actions has to be programmed, that's a given. But giving all these actions to actors and having them make decisions and do these actions and testing all this out over and over again while making tweaks is basically what AAA game dev these days is all about. However, if there were a nice decision tree editor for an engine it would open up the door for some complex gameplay done by designers. Some actions & conditions can be basic and reusable but some are still very specific to the game too and need to be programmed still.

 

This is what I like about "action" based or component based programming. You program very specific low level actions and you then chain together to make complex actions. If something is complex what as programmers are we told to do? Break it into pieces. Actions and behaviour trees basically do that to the extreme.

 

On the other hand, making such a system can be complex to the point of saying what's the point :)

Link to comment
Share on other sites

Guest Red Ocktober

it's only complex if you make it complex... bear with me here...

 

the call seeking logic of of a game object... say a soldier for example, would be implemented the top of the class hierarchy of all related objects, and thus, it only needs to be implemented at that level... all other behaviors inherited from ancestors would be available, included those inherited from the base classes (being almost "instinctive" and not needing to be considered in the task at hand)... thus reducing the complexity of a implementing a single level of behavior, and allowing the coder to focus on the immediate task...

 

reducing the complexity, while at the same time allowing a potentially incredibly amount of complexity to be available to an instance at the top level of class hierarchy...

 

this is why i'm such an oop devotee... it's elegant in its simplicity... it's like magic happy.png in its realization... especially when you see examples from the top of the hierarchy running around interacting with other objects in the world... doing exactly what they, and all of their ancestors were programmed to do... interacting in ways that were implemented way back when at the dawn of time, when that objects ancestors were initially created...

 

i wonder if this is how god feels when she looks down upon mankind... biggrin.pngbiggrin.pngbiggrin.png

 

--Mike

Link to comment
Share on other sites

I wrote a library that inverts callbacks and allows you to compose their behavior. On the web side of things we use them all the time. They are called promises. Lua has awesome continuations but ultimately they cannot be controlled well or composed so i created a promise library and an event emitter library. I don't know why the game world hasn't caught on. I feel like I am taking crazy pills. It's like c++/game devs live in a vacuum.

 

https://github.com/friesencr/lua_promise

Link to comment
Share on other sites

Not everything is obvious at first glance, you had little time for C++ a few years back Josh and now its one of your biggest selling points! Our perception of things change sometimes when we actually start using them, sometimes they don't and it just re-enforces our initial suspicions.

I still think C++ is terrible, it's just the best thing we've got. tongue.png

 

In my early days of DarkBasic I got involved early on in a project where our lead programmer would use all these vague but amazing-sounding terms of all the stuff he was going to build. No one understood any of it, but he described it all so confidently and it sounded so advanced we all believed him. The game was going to be a cross between Far Cry and Terminator 2...thus the name "Metal" came into play. We were planning to have liquid metal environments that shifted around the player, which was an idiotic idea, but was more exciting than dealing with the reality that we had no idea how to code a game. Our lead programmer talked and talked and never produced any code. Everyone left, and I went on to actually publish a generic tournament FPS game written with spaghetti code in Blitz3D. The game I made had nothing to do with our original concept, but it actually shipped as a commercial product on store shelves. (In England, anyways.)

 

Which was the more impressive game? The one that had infeasible features and grand architecture ideas, or the one that actually existed in reality and could be played?

 

This guy on the Blitz3D forum keeps making games and putting them out. They're terrible, but he's making games:

http://blitzmax.com/...c=94230#1080631

 

The Blitz3D regulars just heckle him, while talking about their imaginary game that is so much better, if only they could get around to coding it. I always stick up for him because his existing game is a million times better than their non-existent theoretical concept.

 

If you want to be a musician, make music. Even if it's terrible, just force yourself to write a finished song. The same idea applies to game development. Decide exactly what you want, code it, then release it. Code is a means to an end. When I started doing music, I actually took what I learned from seeing indie game developers and applied it, telling myself I would not get caught up in the details, and would just focus on making finished songs with really low production quality.

 

Anyways, I forget what my point was and I'm getting off-topic. That's the point of view I come from, and why my advice is always to just decide what you want and code it, and never try to code for features you haven't yet defined. For Darkness Awaits, we just want goblins that run up to you when you are within a certain range and start hitting you with an axe until one of you is dead. That's our game, so that's what we're coding. These are the kinds of discussions we are having right now:

I need a way for the enemy goblin to turn and face the player when the goblin is attacking. I tried calling follow with speed and acceleration set to 0.0 but the goblin still moves.

It's not very fancy-sounding, but we're getting through it.

  • Upvote 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Guest Red Ocktober
It's like c++/game devs live in a vacuum.

 

LOLZ biggrin.png good one...

 

i only code in c++... i develop in colloquial american english...

 

i write out the logic before i lay down a single line of code... and i use that as comments in c++

or BlitzBASIC, or VisualBASIC, or Delphi, or (uggg) LUA, or whatever the situation calls for...

 

the point i was making is that the language is pretty much irrelevant... it's the logic that counts...

 

a good OOP hierarchy that allows you to compartmentalize logic as required really helps...

 

--Mike

Link to comment
Share on other sites

OOP is a good thing but you shouldn't "over-oop", if you are a hammer - every problem looks like a nail (hope this is correct saying?).

 

In my opinion for an RPG/MMORPG or modable RTS an Data Driven or Component Based System is much better then doing strict oop and doing a class for every possibility.

  • Upvote 2
Link to comment
Share on other sites

Guest Red Ocktober
if you are a hammer - every problem looks like a nail

OOP is not really analogous to a hammer, or a saw... the proper analogy would be the tool shed itself... but only a master craftsman would realize this... cool.png

 

you are right about the "over ooping" part though... anything can be carried too far...

 

data driven logic has its place... and its place is in a good class structured hierarchy biggrin.pngbiggrin.png

 

 

--Mike

Link to comment
Share on other sites

I still think C++ is terrible, it's just the best thing we've got. tongue.png

Ha ... well fair comment biggrin.png But please don't make your next project a new language that's going to revolutionize the gaming world, because, as we know .... Mika has already done it tongue.png

Intel Core i5 2.66 GHz, Asus P7P55D, 8Gb DDR3 RAM, GTX460 1Gb DDR5, Windows 7 (x64), LE Editor, GMax, 3DWS, UU3D Pro, Texture Maker Pro, Shader Map Pro. Development language: C/C++

Link to comment
Share on other sites

Now that we're nearly done with our AI, I have a little more to talk about.

 

There are so many little logical combinations we have. When you hit an enemy, they go into the "hurt" state. The pain animation plays, they play a sound, and are unable to move or attack until the animation finishes.

 

We decided we didn't want the player to pause when hit, like the enemies do, because it was annoying and he could get "frozen" if a bunch of enemies attacked at the same time. We did want the player's hurt animation to get played, but only if he was standing still and not walking, running, or attacking. This totally breaks our idea of clearly defined states.

 

Instead, we just set an extra variable called "stunned" and set it to true when he is hit. When the pain animation completes (if it is used) this variable gets set back to false. While this variable is true, the player cannot start the pain animation again, though he can be hurt again.

 

A system where he was in hurt, idle, walking, or attacking states would not handle that. We might be able to have multiple states or a hierarchy of additive states, but that is so confusing.

 

What I really recommend is compartmentalized spaghetti and iteration. Compartmentalization keeps your code from becoming unmanageable. Our AI script (or a self-contained C++ class) can get as confusing as it needs to be, and it won't add any complexity to the rest of the code. Just be very careful about the interaction between those classes or scripts. The only place where our AI scripts interact is when they call the Script:Hurt() function on another entity, to deliver some pain.

 

The iteration part means just play your game over and over again, test, test, test, and find any little issues. There's no way you can predict all the little nuances like what I described above, but once you get in there and start testing you will find lots of little things you didn't think about. It's actually pretty fun, and you can endlessly tweak and adjust your AI until you're happy with it. This has always been my favorite part of game programming and I hope we get more people to that level so you can enjoy it yourself.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Instead, we just set an extra variable called "stunned"

 

This is why I like separating all these things into their own classes. As things get more complicated you end up with many of these, what I call, helper, variables and things start getting messy fast and if you have all this basically in 1 class it can end up getting very big and unmanageable. So what you are doing is basically what I was talking about.

  • Upvote 1
Link to comment
Share on other sites

Interesting approach Josh, i would use a plain effect combined with auras. For example, enemy goblin has a "stunstrike" which stuns your player, to prevent a stunlock i would create an aura "aurastun" while this aura lasts on a player/object he cant be stunned again. (if you want a "safetime" after the stun, you could create another aura "poststunaura" which does the same like the "stunaura" but allows to interact)

 

To be more clearly, the effect "stunstrike" only does the graphic+sound and create an "stunaura" (if possible) on the object which freezes the interactions as long as its active.

 

Thats my idea how to manage this stuff.

Link to comment
Share on other sites

Guest Red Ocktober
We decided we didn't want the player to pause when hit, like the enemies do, because it was annoying and he could get "frozen" if a bunch of enemies attacked at the same time. We did want the player's hurt animation to get played, but only if he was standing still and not walking, running, or attacking. This totally breaks our idea of clearly defined states.

 

yes it does... and that's not a good thing...

 

this is happening partly because of the what i see as being a fundamental misunderstanding of what the Player class is (if there is in fact a Player class at all)...

 

the Player class shouldn't be that which is applied to the lil guy you see running around the game world... the Player class, as i see it, is an interface to the real world human that is playing the game... designed only to provide a basis for housekeeping and data management for general configuration and other necessities that the game class requires...

 

the Antagonist class (Hero class, or whatever you want to call it) should be the source from which the instance of the game object that is the guy the Player controls... the one that we see running around the game world... and all it's behaviors should be self contained within the class definition...

 

We did want the player's hurt animation to get played, but only if he was standing still and not walking, running, or attacking. This totally breaks our idea of clearly defined states.

 

in the real world an Antagonist can still run or walk if he/she is being attacked and sustaining injury... the "hurt" animation should only commence when the Antagonist has been injured beyond a certain point... the point at which it can no longer walk, run, or attack...

 

degraded walk, run, and attack behaviors could be implemented (along with associated animations) for varying levels of injury... depending on how far you want go with it...

 

this should all be part of the Antagonist (Hero class) definition... as should the events or messages that would indicate that it had been hit...

 

Just be very careful about the interaction between those classes or scripts.

scripts and/or classes should never interact directly with each other... objects should react only to messages or events as they are triggered (being struck for instance)... directly manipulating a variable (or property) in another script (or class) is just looking for trouble... and confusion...

 

 

yes... it appears as if you are indeed cooking up a big plate of speggetti tongue.png

 

--Mike

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...