Jump to content
Sign in to follow this  
tracy_t

Optimising performance in scripts

Recommended Posts

I have 300+ units moving at once in my new mission (yeah, you read right.) and there's obviously some lag (even on an XP 2600+)

My code is not running fast enough. What I have so far is along these lines: (I have not cut & pasted this from the actual code, this is typed by hand from my work PC so expect some errors):

NB:

AllWestSoldiers is a trigger that covers the entire island, activated by WEST, once

AllResSoldiers is a trigger that covers the entire island, activated by RESISTANCE, once

For each zombie on the map, there is one copy of Movezombie.sqs executing. So, really OFP is "multithreading" 1..200 EAST zombie processes. (The other 100 units are West.)

A new zombie is created every 10 seconds from a spawn point whenever the player is within certain triggers (AKA spawn triggers). There are a maximum of 200 zombies at once on the island.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

; movezombie.sqs

; param 0 = object reference to unit to make a "zombie"

; param 1 = object reference of unit to attack & eat!

_zombie = _this select 0

_target = _this select 1

_zombie setunitpos "UP"

_zombie disableAI "TARGET"

_zombie disableAI "AUTOTARGET"

_zombie allowfleeing 0

#beginmove

_zombie removeallmagazines "Strokefist"

? !alive(_target) : goto "acquirenewtarget"

? _zombie distance _target > 1000: goto "deletezombie"

? _zombie distance _target < 5: goto "eat"

? _zombie distance _target > 5: goto "move"

; This is based on D. Murphy man's zombie code (but with a few tweaks)

#eat

? alive _zombie : goto "end"

? alive _target : goto "acquirenewtarget"

_zombie setpos getpos _target

_zombie addmagazine "Strokefist"; <-- note action may be wrong. I know, but it's right in the game.

_zombie fire "Strokefist"

_target setdamage (getdamage _target) +.2

_target globalchat "AH! OUCH! YA BAS! etc etc"

~3

goto "beginmove"

#move

_zombie domove _target

; Note: The following 2 lines of code aren't in the real

; game yet, but something along those lines will be added.

? _zombie distance _target >500: PlaySound "distantmoan"

? _zombie distance _target <50: PlaySound "nearmoan"

; Gives unit time to move, and OFP time to process other threads

~3

goto "beginmove"

#acquirenewtarget

_target = Player

_y=0

#westloop

? _y >= count list AllWestSoldiers: goto "doRes"

_unit = (list AllWestSoldiers) select _y

? !alive _unit: goto "nextwest"

? _zombie distance _unit < _zombie distance _target: _target = _unit

#nextwest

_y=y+1 ; Question: does y++ work? Hmm...

goto "westloop"

; Now iterate through list of resistance soldiers

#doRes

; same code as for west really, except using AllResSoldiers

;

...

...

; By this point _target will be new zombie target

goto "beginmove"

#deletezombie

~10

DeleteVehicle _zombie

exit

I appreciate that putting _zombie distance _target in a local variable would probably speed things up.

Now that you have seen the code, some questions:

1) Any ideas on how to optimize the code even more? The zombies sometimes don't move when they should.

2) I can't seem to assign list AllWestSoldiers to a variable; the variable seems to get out of sync with the real contents of the list. Any faster ways of list access?

3) Even though zombies are close to the player, they don't always attack when close (ie: call the "eat" function)? Could it be one of my threads are blocking the others?

4) Any other tips? Does OFP interpret the SQS file each time it is used (ie: called) or does it pre-compile the code beforehand?

5) Any technical information on how to optimise script speed? References etc?

Thanks in advance

Traqcy

Share this post


Link to post
Share on other sites
For each zombie on the map, there is one copy of Movezombie.sqs executing

Should I take that as one copy of moviezombie.sqs running on each machine for each zombie or one copy running only on the machine where the zombie is local?

Quote[/b] ]

I appreciate that putting _zombie distance _target in a local variable would probably speed things up.

Agree. Not much, but every bit helps.

Quote[/b] ]

Now that you have seen the code, some questions:

1) Any ideas on how to optimize the code even more? The zombies sometimes don't move when they should.

Couple of notes/possible tweaks:

<ul>

[*] Dont do things globally that need only be done where the zombie is local. (setUnitpos, disableAI, allowFleeing, removeAllMagazines, addMagazine, doMove, setPos)

[*] Move the player cries for help to a "Hit" event handler?

[*] Likewise I suspect the playSound needs to be done on all player machines but not on a dedicated server.

[*] If this script is run on spawn, make it sleep for a random amount of time in the beginning. This will alleviate some of the spawn-time stutterfests that are so common in zombie missions. (Nogova Virus being a good example)

Basically, I'd think about what needs to be done on the server and what needs to be done on the clients and in that way cut down on unnecessary stuff.

Quote[/b] ]

2) I can't seem to assign list AllWestSoldiers to a variable; the variable seems to get out of sync with the real contents of the list. Any faster ways of list access?

<ul>

[*]I'm not entirely sure what happens for you there, but I would move the "list AllWestSoldiers" out of the loop as quick as possible.

[*]Assign a variable the value of "count list AllWestSoldiers" for loop efficiency.

[*] Concatenate the two arrays of players - the AllResSoldiers and AllWestSoldiers. No need to do two travels through them separately, I believe

[*] Add a slight delay in the loop. It chews CPU as it is.

[*] No, OFP scripting does not have the ++ increment operator

Quote[/b] ]

3) Even though zombies are close to the player, they don't always attack when close (ie: call the "eat" function)? Could it be one of my threads are blocking the others?

Is suspect both CPU load issues and local/remote clashes leading to desync and all that loveliness...

Quote[/b] ]

4) Any other tips? Does OFP interpret the SQS file each time it is used (ie: called) or does it pre-compile the code beforehand?

I believe its the former. BIS has hinted to the possibility of some sort of JIT pre-compiling, but if that was/is for functions loaded by preload/preprocessFile only or separate scripts, I dont know.

Quote[/b] ]

5) Any technical information on how to optimise script speed? References etc?

Perhaps... biggrin_o.gif I'd need more info on how the whole system is set up and intended to work. Don't suppose one could get a preview under the table wink_o.gif

Share this post


Link to post
Share on other sites

No, for each ZOMBIE there is a copy of zombie.sqs running on the client! This game isn't meant to be multiplayer (although someone can mofify it if they like. As long as you credit me I don't give a toss)

200 zombies = 200 zombie.sqs's executing. Yes, I know I could do it in a single script iterating through an array but... hmm, maybe I will...

OK, I will send you a pre-release beta of the game; what's your email addy? PM me and I'll send it. But I warn you, it's a big chappie smile_o.gif

Alternatively, can anyone host it for me? I don't mind releasing a beta for other people (read: zombie fanatics) to use in their own missions.

PS: Thank you for your reply & time. Much appreciated.

Share this post


Link to post
Share on other sites

Why bother creating 200 units if the player/players are not near some of them? Create them as the players get nearer to the zombie areas and delete them when the players are too far away.

RED

Share this post


Link to post
Share on other sites

I want to create 200 units because I want to simulate zombies roaming the countryside looking for flesh. Not just the player's flesh but also the AI soldier's!

I don't want the zombies to magically disappear when the player is out of range; I want them to go attack someone else. I can assure you the player will have enough to contend with elsewhere.

Share this post


Link to post
Share on other sites

First why don't you try a test: create all your zombies and give them one long move command each. See if the framerate is still good. I doubt it will be.

Save the distance in a variable _d so you don't run 'distance' all the time

shorten all variable names to one character or as close to that as possible, same with labels.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">#acquirenewtarget

_target = Player

_y=0

#westloop

? _y >= count list AllWestSoldiers: goto "doRes"

_unit = (list AllWestSoldiers) select _y

? !alive _unit: goto "nextwest"

? _zombie distance _unit < _zombie distance _target: _target = _unit

#nextwest

_y=y+1 ; Question: does y++ work? Hmm...

goto "westloop" Can be

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_t=Player

_l=list AllWestSoldiers

_d=_z distance Player

{if(_z distance _x<_d)then{_t=_x;_d=_z distance _x;}}foreach _l

Share this post


Link to post
Share on other sites

Oh God, shorten variable names? That reminds me of C64 BASIC years ago...

I got 100 zombies moving right now and I have ~30fps. There are about 50-70 west soldiers in the melee too.

Not quite as responsive as I'd like, but better than nothing.

My benchmark is 5639, so you lot will be able to guess how this will run on your PC.

Mind you, I have a Radeon 9700 Pro, soz....

PS: if.. then only works in a function, does it not. So putting it in a for..each in an SQS (as I have now) won't work.

Would anyone like a copy of the work "as-is" ?

Share this post


Link to post
Share on other sites

Funny you mention half life - there's erm, some similarities in there.... you will see what I mean soon enough.... *cough*

Share this post


Link to post
Share on other sites

hehe lol

Next we'll have small crabs running around, looking for heads to jump on so that they can become zombies

Share this post


Link to post
Share on other sites
Oh God, shorten variable names? That reminds me of C64 BASIC years ago...

Yep.

Quote[/b] ]snip

PS: if.. then only works in a function, does it not. So putting it in a for..each in an SQS (as I have now) won't work.

Nope. Try it, a one liner does work in sqs the same as a call {string}. smile_o.gif

Share this post


Link to post
Share on other sites

WHAAAAT? wow_o.gif

You can use: If Then Else in functions toooooo   tounge_o.gif  

Surely those work in ordinary sqs scripts aswell  wink_o.gif

~S~ CD

Share this post


Link to post
Share on other sites

yeah, as long as you don't drop in a newline in there.

{

_bla=_x;

}foreach whatever;

does not work in sqs, but

{_bla=_x;}foreach whatever;

does work in sqs.

tounge_o.gif

Share this post


Link to post
Share on other sites

Optimize with zombies, they are slow, no?

OFP with zombies curious, try an army of zombies with ... good idea isn'it?

Never mind sun shine for everybody...

Share this post


Link to post
Share on other sites

LOL, yeah, too many, I would try to have no more than 100 tops.

teleport them around depending on a distance like 1Km (anyway I don't like Zombie stuff personally, but who cares)

Share this post


Link to post
Share on other sites

I see what you're trying to accomplish, but remember that the important part is what the player sees or perceives to be happening whether it really is or not.  The appearance that there is mass zombie movements going on can sort of be faked, just make it look like that to the player.

Here's some of my stuff from nogova virus, and some of it's hacked together from the original code and I've never really optimized it much since it seems to work in the way I make it.  Basically it uses Units WGroup which I group in the init of each soldier.  The zombies spawn within a defineable distance of them.  You can make it farther as well, but 70 is just at the cusp of vision with all the fog and such.

Anyway, I'll copy what I use for creation and attack. BTW the Hugespawn and Bigspawn are just global vars to make more or less zombies appear at once...however you wish to set the mood. smile_o.gif

zombies.sqs (creates the undead)

Quote[/b] ]

_dist = 70

ZombieTypes = ["TCP_TestDog","Civilian","TCP_TestDog","Civilian2&quot

;

;,"Civilian3","Civilian4","Civilian5","Woman1",&am

p;qu

ot;Woman2","Woman3","Woman4","Woman5","Civilian6&a

mp;q

uot;,"Civilian7"]

BigSpawn = FALSE

HugeSpawn = FALSE

_wait = 5

?!Local Server : Exit

#Init

~2

?!z_startspawn : Goto "Init"

~_wait

;;Playsound "whisper"

UnitList = Units WGroup

_x = 0

_y = 0

_one = TRUE

_two = FALSE

_three = FALSE

#GetAmount

? param1 == 1 : _amount = 1; _wait = 60

? param1 == 2 : _amount = 2; _wait = 45

? param1 == 3 : _amount = 2; _wait = 40

? param1 == 4 : _amount = 3; _wait = 30

? param1 == 1 AND BigSpawn : _amount = 4; _wait = 60

? param1 == 2 AND BigSpawn : _amount = 8; _wait = 45

? param1 == 3 AND BigSpawn : _amount = 10; _wait = 40

? param1 == 4 AND BigSpawn : _amount = 13; _wait = 30

? param1 == 1 AND HugeSpawn : _amount = 4; _wait = 20

? param1 == 2 AND HugeSpawn : _amount = 8; _wait = 15

? param1 == 3 AND HugeSpawn : _amount = 8; _wait = 10

? param1 == 4 AND HugeSpawn : _amount = 8; _wait = 10

;;hint format["wait: %1\nBig: %2",_wait,Bigspawn]

?groupatend : _amount = 16

#SpawnLoop

~.1

_unit = (Units WGroup) Select _y

?_one : _group = ZGroupA; _dir = (GetDir _unit); _one = FALSE; _two = TRUE; Goto "Create"

?_two : _group = ZGroupB; _dir = (GetDir _unit) + 45; _two = FALSE; _three = TRUE; Goto "Create"

?_three : _group = ZGroupC; _dir = (GetDir _unit) - 45; _three = FALSE; _one = TRUE

#Create

;;hint Format["Creating for unit: (%3) %1\n_dir: %2(%5)\namount: %4",Name _unit,_cdir,_y,_amount,_dir]

_zType = ZombieTypes Select _x

_posX = (Getpos _unit Select 0) + _dist * sin _dir

_posY = (Getpos _unit Select 1) + _dist * cos _dir

Unit = _unit

?Count List Zombies < 50 : _zType createunit [[_posX,_posY,0],_group,"zombie = this; [this,Unit] exec {zombieattack.sqs}; this addweapon {strokefist}; this setbehaviour {careless}; this setunitpos {up};this setdammage 0.80;[this] Join GrpNull"]

_ran = Random 10

?_ran > 9.5 AND HugeSpawn : zombieburn = zombie; PublicVariable "ZombieBurn"

_x = _x + 1

?_x >= Count ZombieTypes : _x = 0

_amount = _amount - 1

?_amount > 0 : Goto "SpawnLoop"

Goto "NextUnit"

#NextUnit

_y = _y + 1

?_y >= (Count Units WGroup) : Goto "Init"

Goto "GetAmount"

Exit

zombieattack.sqs

Quote[/b] ]

?!Local Server : Exit

_zombie = _this Select 0

_factor = 4

_zombie SetDammage .8

[_zombie] join GrpNull

?_zombie in list PetroviceE : _zombie SetPos Getpos mark1

_zombie SetBehaviour "CARELESS"

_group = Group _zombie

_group SetSpeedMode "FULL"

_bodytime = 10

? param2 == 1:_bodytime = 1

? param2 == 2:_bodytime = 10

? param2 == 3:_bodytime = 20

? param2 == 4:_bodytime = 40

#Main

?KillAll : _zombie SetDammage 1

_zombie Removemagazines "Strokefist"

?(!alive _zombie) : goto "End"

?_zombie Distance _target > 200 : goto "End"

?_zombie in list PetroviceE : _zombie SetPos Getpos mark1

#FindUnit

_y = 1

_target = Leader WGroup

#FindLoop

~.01

_unit = Units WGroup Select _y

;;hint format["%1\nsld: %2",_unit,_target]

?_zombie Distance _unit < _zombie Distance _target : _target = _unit

_y = _y + 1

?!Alive _zombie : Goto "End"

?_y < Count Units WGroup : Goto "FindLoop"

?_zombie Distance _target > 170 : Goto "end"

?!Alive _target OR IsNull _target : goto "FindUnit"

?format["%1",_target] == "scalar bool array string 0xfcffffef" : Goto "End"

#Move

? _zombie distance _target <= 1 : _zombie addMagazine "StrokeFist"; goto "attack"

? _zombie distance _target > 1 && _zombie distance _target <= 5 : goto "engage"

? _zombie distance _target > 5 : _zombie doMove getPos _target

~3

Goto "Main"

#engage

;;?_target in APC:goto "main"

;;?_target in fueltruck:goto "main"

_nX = getPos _target select 0

_nY = getPos _target select 1

_zombie setPos [_nX, _nY]

goto "main"

#attack

? _zombie distance _target > 1 : goto "main"

_zombie fire "StrokeFist"

_target setDammage (getDammage _target + 0.1)

~1

_zombie removeMagazines "StrokeFist"

goto "main"

#End

~_bodytime

DeleteVehicle _zombie

zombieplayercheck.sqs (this gives the attack msgs when a zombie is near a player and runs on each client.)

Quote[/b] ]

_unit = _this Select 0

_zlist = ZombieTypes

#Init

_x = 0

#Loop

~.001

_ztype = _zlist Select _x

_zombie = NearestObject [_unit,_ztype]

?IsNull _zombie : goto "Next"

;;Hint Format ["unit: %1\nztype: %2\nzomb: %3\dist: %4",_unit,_ztype,_zombie,_zombie distance _unit]

?_zombie Distance _unit < 2 and Alive _zombie : Goto "Attacked"

#Next

_x = _x + 1

?_x < Count _zlist : Goto "Loop"

Goto "Init"

#Attacked

_rn = Random 12

?_rn >= 0 and _rn < 2 : _msg = "THAT THING IS TOUCHING ME IN MY DIRTY PLACE!"

?_rn >= 2 and _rn < 4 : _msg = "AHHHH IT'S ON ME! HEEEELP!!"

?_rn >= 4 and _rn < 6 : _msg = "ARGHH!! IT'S CHEWING OFF MY ARM!!"

?_rn >= 6 and _rn < 8 : _msg = "I'M BEING EATEN!! AIEEEEE!"

?_rn >= 8 and _rn < 10 : _msg = "A ZOMBIE ATE MY PEE PEE!!"

?_rn >= 10 and _rn <= 12 : _msg = "IT'S EATING A HOLE IN MY PE SHORTS!!"

_unit GroupChat _msg

~5

Goto "Loop"

Share this post


Link to post
Share on other sites
Guest

Ya...I know what you are after. Ultimately though if the zombie eats the AI, and the player isn't there to see it, does it still matter wink_o.giftounge_o.gif (tree falling in woods....)

Share this post


Link to post
Share on other sites

Hi Karillion, I looked at your scripts before (I unpbo'd the Nogova virus mission to see how the zombies were created), but they weren't suited to what I needed.

Anyway, here's how I get 100 + zombies on screen at once.

Monitorarea.sqs: monitors a resistance player in a spawn area. To be replaced by more optimal code soon.

group1 - group20 are individual EAST soldiers, hidden away on a secluded part of the island. They are only there so zombies can join the EAST group and be shot at by West, although since I created my zombie addon they are pretty much redundant.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_trig = _this select 0

_opos = getpos _trig

#sp

? count list (_trig) > 0: goto "az"

~2

goto "sp"

#az

_gc=1

#fg

?_gc==1:_gp = group1

?_gc==2:_gp = group2

?_gc==3:_gp = group3

?_gc==4:_gp = group4

?_gc==5:_gp = group5

?_gc==6:_gp = group6

?_gc==7:_gp = group7

?_gc==8:_gp = group8

?_gc==9:_gp = group9

?_gc==10:_gp = group10

?_gc==11:_gp = group11

?_gc==12:_gp = group12

?_gc==13:_gp = group13

?_gc==14:_gp = group14

?_gc==15:_gp = group15

?_gc==16:_gp = group16

?_gc==17:_gp = group17

?_gc==18:_gp = group18

?_gc==19:_gp = group19

?_gc==20:_gp = group20

_nu = count units _gp

? _nu < 10: goto "cz"

~2

_gc = _gc+1

? _gc < 15: goto "fg"

exit

#cz

_zi = random(1) * 10

_zType = ZombieTypes select _zi

[_zType, _opos, _gp] exec "createzombie.sqs"

~5

goto "sp"

Createzombie.sqs:

First parameter is type of zombie to create;  I specially created a zombie addon for operation flashpoint which up-armors the zombies leg, body, arms etc (but not the head). You can have it if you like.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_zType = _this select 0

_opos = _this select 1

_group = _this select 2

_zType createunit [_opos,_group,"Zombie = this;"]

zombie SetBehaviour "CARELESS"

zombie DisableAI "TARGET"

zombie DisableAI "AUTOTARGET"

zombie allowfleeing 0

zombie SetMimic "Agresive"

dostop zombie

_group = Group _zombie

_group SetSpeedMode "FULL"

[zombie] exec "movezombie.sqs"

exit

movezombie.sqs:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_z = _this Select 0

_t = _this Select 1

#Main

~1

?(!alive _z) : goto "End"

?(_t != objNULL) && (alive _t) goto "move"

_tig = false

_t = player

_pt = (list resistancesoldiers) + (list westsoldiers)

_y = 0

_uc = count _pt

#loop

~2

? _y>= _uc: goto "premove"

_u = _pt select _y

? (!alive _u) : goto "nextunit"

? _z distance _u < _z distance _t: _t = _u

#nextunit

_y=_y+1

goto "loop"

#premove

? _t in units group player: _tig = true

_z dowatch _t

#move

_d = _z distance _t

? _d > 1000 : Goto "end"

? _d > 5 : goto "walk"

? _d < 6 : goto "engage"

Goto "Main"

#walk

_z domove getpos _t

~2

goto "Main"

#engage

? !alive _z: goto "end"

_z removeMagazines "StrokeFist"

_z setPos getpos _t

_z addMagazine "StrokeFist"

_z fire "StrokeFist"

~3

_t setDammage (getDammage _t + 0.2)

~1

goto "Main"

#End

~5

DeleteVehicle _z

exit

PS: I see we've both re-used D-Murphy man's zombie code in parts smile_o.gif

S.

Share this post


Link to post
Share on other sites

PS:

To answer your question, yes it still matters if the AI is killed or not.

The WEST AI soldiers won't be on your side - they will shoot you down like a dog if they see you near their base(s). If they couldn't be killed then the game would be unrealistic.

Share this post


Link to post
Share on other sites

Actually Karillion,

your zombieplayercheck.sqs file has given me an idea on how to improve performance in my game... !

Probably will speed it up by a significant factor lol! Just goes to show an old dog like me can learn new tricks smile_o.gif

I will of course credit you appropriately.

In return, would you like any help with Sound FX, or music? I have just about every well-known zombie movie ever made to hand, so I'm yer man for getting desired FX...

Cheers,

Scott

Share this post


Link to post
Share on other sites

OFP works on probabilities and some random factors right? One AI running into another AI came about because they each had a destination and happened to be in the same area around the same time. One noticed the other and they reacted.

A suggestion would be to model this without actually drawing it if the player can't see it. It would still be true to the idea that zombies are wandering the countryside and killing AI units they come across or getting killed by them.

You could run a test with everything as you have it now but even more zombies if that's what you are going for. Keep track of where zombies or other AI are killed, how often, etc. and use that data with some randomization to model a virtual war going on outside of the player's view. If your test finds that x% of zombies kill so many other AI and vice versa and you find the average period of time before another death occurs you can add some random elements to that and then script it.

There will be (x% +/- random %) of each team left for the player to run into, dead bodies for the player to see, and a different experience for the player each time he moves through that mission. Could figure out how many bodies you want the player to see at any given time and move them around as needed. The goal is to make it seem like there are many more zombies/AI to the player while also trying to keep it "realistic" for the player to come across live/dead AI and scenes of battles.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×