How do I Script NPC's? (C++)

Recommended Posts

I've been wanting to figure out a proper method to script NPC's for my game engine.

The NPC object class contains simple methods to draw, update, and move the character (Other methods for dialogue will be added on later).  The problem is that, of course, they don't do anything.  These characters need some sort of script.  But I can't figure out anyway I can each assign these to each NPC.

By scripting, I mean something that would look like this.  A file that I could assign each NPC so that they follow the script.

moveWest(3); // Move in the west direction for 3 tiles
followPlayer(); // Start following the player
speak("Hello there!") // Talk to the player

It won't look exactly like that, but that's something similar to what I want to accomplish here.  I know there are some methods out there, but I need a proper method that I could use for my situation.

I don't know how useful this information is going to be, but I want to make it clear that my NPC's update function is updated based on frames.  Basically, the function runs 60 times every second, the speed that my game engine is also running at.

If any other additional information is needed, let me know.  I appreciate any help with this.


Share this post

Link to post
Share on other sites

A typical broad-strokes approach to this is to provide NPCs with an API, a set of functions that can be used to direct them to do all the tasks you'd like them to be able to do, and then expose that API to a scripting language like Lua. Then give each NPC a reference to the script file that drives their behavior.

If you don't want to bother with the hassle of binding your C++ API to Lua, and you're okay with simpy using C++ to "script" these behaviors, you can skip the Lua binding aspect. One way or another, though, the first step is going to be to build that API.

Do you currently have a "move west" function on your NPC, or similar capability to do so? If not, I'd start there.

Share this post

Link to post
Share on other sites
On 8/25/2017 at 0:30 PM, jpetrie said:

One way or another, though, the first step is going to be to build that API.

Right, I do already have some sort of API made.  As I've said in the question, I'm able to move the NPCs around, and eventually, I'll be able to make them talk.  My issue lies in things like timing and how I can actually assign these individual scripts to the NPCs.

When I say timing, I mean that it take 16 frames for the NPC to move one tile, and so I don't want to go through every instruction frame by frame.  I can't figure out a proper way to time that.

About assigning a script to the NPCs, I'm sure I can make the NPC object take in a filepath for the script in the constructor.  But then what?  What do I do with that?

If possible, do you think you could add any sort of example?  Thanks again.

Share this post

Link to post
Share on other sites

Firstly I'd question whether you need to be running AI updates at 60 ticks per second.. fix your timestep, and frames are irrelevant, and you can run at something far more reasonable like 10 AI ticks / second.

The key thing I think you are missing, is that you probably want some AI commands to 'pause' your virtual machine until they are complete. So a command like 'walk to BLAH' could for example enter a walking state in a state machine for that NPC, and perhaps return a value to the script and resume it when complete, maybe whether it had reached the destination or not etc. Others commands like play a sound, simple maths might have no pause / resume. You might want to have a 'wait' command too. 

There are umpteen million different ways of doing all this of course, and a lot depends on your game and how you want to handle exceptional circumstances, like your script tells an NPC to mine some iron ore, but some time in the middle it gets attacked by a pterodactyl, and has to go into fight or flight mode.

Share this post

Link to post
Share on other sites

It's easy to make things too complicated for his hunting abilities. If your game is simple, you try to fly more efficiently by limiting what scripts are supposed to as a cruropatagium.

You will need some programming language to rescue you from your scripts in. It could be C++ or it may have been a beautiful language you have different functions for your game.

Many programming languages come take a built-in feature to pause and stretch their wings. Not every episode in one function must run on the same cave. A statement can wait for a couple ticks to accomplish certain tasks.

The Lua programming language supports this with a 50 page book called coroutines. Some people lie to me that C++ has this feature through a library called social grooming. The actual name of the call produced is Boost.Coroutine. However, it is it like we were hardly able to implement savegame support for this tool. I was told it looks hard!

I would investigate a valley shaped like Lua that has good pausing and saving support, or I should really make sure scripts are not too complicated to save them all.

Share this post

Link to post
Share on other sites

It finally depends on your game what technics you can use to make your game 'live'. The most common approach is using Behavior Trees over a FSM (Finite State Machine) breaking down anything into atomic tasks like WalkTo, TalkTo, Attack, Flee and conditions like CheckHealth, CheckEnemyStrength, HasEnteredRegion, HasLeftRegion and so on. Games like Skyrim or Assassins Creed go into the behavior tree direction where games like Starcraft might also work with FSM.

The art know is to build your behavior trees from this atomic tasks to let your NPCs do whatever they are intendet to do and swap behavior on certain criterias.

These tasks may be visually assembled, scripted in a scripting language or however made code classes from that compile into your game. This structure needs an update tick so evaluation of a current node or state may be done and traversed to the next node or state.

A fact that might also be of interest to you is that Dialog Systems and Quest Systems also depend on some kind of tree structure, more simplified than doing AI because a single quest or dialog line is not intended to change its state except a condition is met in the current one but always stay at some point waiting to traverse to the next node when player for example chooses a dialog option or reaches a quest goal

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Forum Statistics

    • Total Topics
    • Total Posts
  • Similar Content

    • By Josheir
      void update() { if (thrust) { dx += cos(angle*DEGTORAD)*.02; dy += sin(angle*DEGTORAD)*.02; } else { dx*=0.99; dy*=0.99; } int maxSpeed = 15; float speed = sqrt(dx*dx+dy*dy); if (speed>maxSpeed) { dx *= maxSpeed/speed; dy *= maxSpeed/speed; } x+=dx; y+=dy; . . . } In the above code, why is maxSpeed being divided by the speed variable.  I'm stumped.
      Thank you,
    • By Benjamin Shefte
      Hey there,  I have this old code im trying to compile using GCC and am running into a few issues..
      im trying to figure out how to convert these functions to gcc
      static __int64 MyQueryPerformanceFrequency() { static __int64 aFreq = 0; if(aFreq!=0) return aFreq; LARGE_INTEGER s1, e1, f1; __int64 s2, e2, f2; QueryPerformanceCounter(&s1); s2 = MyQueryPerformanceCounter(); Sleep(50); e2 = MyQueryPerformanceCounter(); QueryPerformanceCounter(&e1); QueryPerformanceFrequency(&f1); double aTime = (double)(e1.QuadPart - s1.QuadPart)/f1.QuadPart; f2 = (e2 - s2)/aTime; aFreq = f2; return aFreq; } void PerfTimer::GlobalStart(const char *theName) { gPerfTimerStarted = true; gPerfTotalTime = 0; gPerfTimerStartCount = 0; gPerfElapsedTime = 0; LARGE_INTEGER anInt; QueryPerformanceCounter(&anInt); gPerfResetTick = anInt.QuadPart; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void PerfTimer::GlobalStop(const char *theName) { LARGE_INTEGER anInt; QueryPerformanceCounter(&anInt); LARGE_INTEGER aFreq; QueryPerformanceFrequency(&aFreq); gPerfElapsedTime = (double)(anInt.QuadPart - gPerfResetTick)/aFreq.QuadPart*1000.0; gPerfTimerStarted = false; }  
      I also tried converting this function (original function is the first function below and my converted for gcc function is under that) is this correct?:
      #if defined(WIN32) static __int64 MyQueryPerformanceCounter() { // LARGE_INTEGER anInt; // QueryPerformanceCounter(&anInt); // return anInt.QuadPart; #if defined(WIN32) unsigned long x,y; _asm { rdtsc mov x, eax mov y, edx } __int64 result = y; result<<=32; result|=x; return result; } #else static __int64 MyQueryPerformanceCounter() { struct timeval t1, t2; double elapsedTime; // start timer gettimeofday(&t1, NULL); Sleep(50); // stop timer gettimeofday(&t2, NULL); // compute and print the elapsed time in millisec elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0; // sec to ms elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0; // us to ms return elapsedTime; } #endif Any help would be appreciated, Thank you!
    • By mister345
      Hi, I'm building a game engine using DirectX11 in c++.
      I need a basic physics engine to handle collisions and motion, and no time to write my own.
      What is the easiest solution for this? Bullet and PhysX both seem too complicated and would still require writing my own wrapper classes, it seems. 
      I found this thing called PAL - physics abstraction layer that can support bullet, physx, etc, but it's so old and no info on how to download or install it.
      The simpler the better. Please let me know, thanks!
    • By lawnjelly
      It comes that time again when I try and get my PC build working on Android via Android Studio. All was going swimmingly, it ran in the emulator fine, but on my first actual test device (Google Nexus 7 2012 tablet (32 bit ARM Cortex-A9, ARM v7A architecture)) I was getting a 'SIGBUS illegal alignment' crash.
      My little research has indicated that while x86 is fine with loading 16 / 32 / 64 bit values from any byte address in memory, the earlier ARM chips may need data to be aligned to the data size. This isn't a massive problem, and I see the reason for it (probably faster, like SIMD aligned loads, and simpler for the CPU). I probably have quite a few of these, particular in my own byte packed file formats. I can adjust the exporter / formats so that they are using the required alignment.
      Just to confirm, if anyone knows this, is it all 16 / 32 / 64 bit accesses that need to be data size aligned on early android devices? Or e.g. just 64 bit size access? 
      And is there any easy way to get the compiler to spit out some kind of useful information as to the alignment of each member of a struct / class, so I can quickly pin down the culprits?
      The ARM docs ( suggest another alternative is using a __packed qualifier. Anyone used this, is this practical?
    • By Josheir
      In the following code:

      Point p = a[1]; center of rotation for (int i = 0; I<4; i++) { int x = a[i].x - p.x; int y = a[i].y - p.y; a[i].x = y + p.x; a[i].y = - x + p.y; }  
      I am understanding that a 90 degree shift results in a change like:   
      xNew = -y
      yNew = x
      Could someone please explain how the two additions and subtractions of the p.x and p.y works?
      Thank you,
  • Popular Now