Jump to content

[LE] Using one large script vs. Several Small scripts


Recommended Posts

When it comes to performance, script latency, and functionality, is it better to use one large script that contains all the variables and data for many different scenarios or is it better to have several smaller scripts that fire once certain specific conditions are made?

 

I recently created a script which adds a proper formation for a group of soldiers. After having initial success with it, I decided to scale up and add an exponentially higher amount of actors to that same script and essentially copied and pasted what I had initially that was working, simply adding these new actors to the script and giving them to run the same function as the initial group in testing. However, I was met with much worse script functionality and very poor script latency when it came to testing.

 

Should I have created multiple quests that fire their own individual script when a condition is met (ie. when a specified actor is loaded) rather than having one large script for the computer to sift through? Is there a way to make it so that you have a series of scripts that can constantly turn on or off based on conditions that are met? So rather than having on large script running in the background at all times, it is smaller scripts that run temporarily and are disabled when needed. For example, a script for a particular patrol group will run only when it is loaded in the player's cell and will stop once they leave the cell, but can be reactivated once the player is in the same cells as theirs.

 

 

Note: I started with 22 actors when I was developing the script to function properly, and once things worked well all I did was simply scale up, adding over 900 new actor properties and assigning the same exact function to the new actor properties, but now the actors are not reacting nearly as quick to the script as they should have.

Edited by Guest
Link to comment
Share on other sites

I'm guessing that you are referring to this post:



There are a lot of things that are wrong here, but i will just meantion the most importants.

1) You should always take in account that players won't just have your mod, but they could have hundreds of mods, with hundreds of heavy scripted quests, abilities, scenes, weapons, spells, followers.... etc.

Which is a strong reason that someone would use a combat/war mod like yours to test/see their stuff in action.

For example: I would play your mod just to see my X-Beyond droid, which carries a 1200+ line combat script, decimate an entire army all by her self, and to see repeatedly some of her cool abilities which fire only under certain conditions and when she is in a critical state.


2) You are doing something that the game engine is not made for, and you are doing it on a large scale. Just because something can be done it doesn't mean that it safe to do it.


Don't you think that over the 10 years of Skyrim life a lot of experienced modders had the idea to do something similar in the past, but they were never further developed, there must be a reason behind it...

I personally have removed from my mod a large combat scene like this for a bunch of reasons, but mainly for the 'No Consistency' on all systems.



3) You are pulling every 1 sec, and you have a large amount of scripts doing the same thing.

You are giving the system a reason to have a stroke...


4) And a bunch of other things...


Your mod is becoming a recipe for ctds, freezes and corruption.


All "Register" functions have the drawback that can corrupt the save file under certain conditions.


The optimized way to do this is to have 1 "Master Controller" script to handle your troops formation + script pulling, this means to have only 1 script 'Registering' instead of a 100, this does have the disadvantage that there will be different actors responses according to the amount of actors and the system the script is in + the amount of other active scripts at that given point (weaker sys vs strong sys), but it's a million times safer, more stable and reliable than to have a bunch of individual scripts firing.


And do all this in "targeted combat scenes" and to not exaggerate beyond what the game engine is build for, taking always in account the consistency of "Scene's Execution" in ALL systems not only on your system with your 'load order', but on ALL PCs with ALL possible mods set ups.


You need to always have in the back of your mind the word "Optimization", and do your things with the least possible amount of system resources.

Edited by maxarturo
Link to comment
Share on other sites

patrick03079 wrote: "is it better to use one large script that contains all the variables and data for many different scenarios or is it better to have several smaller scripts that fire once certain specific conditions are made?"

 

(1) It depends on the purpose what the script is doing. In your case with many script instances running at the same time,

I would prefer a script that is using states to reduce activity in papyrus engine.

 

(2) By the way what did you meant with: "simply scale up, adding over 900 new actor properties"?

 

(3) The code snippet posted by you in the other thread (linked by maxarturo) can be tweaked for runtime. Suggestions as follow:

version 1

 

;-----------------------------------------------------------
Float FUNCTION GetRotationSpeed(Actor leader, Actor soldier)
;-----------------------------------------------------------
; https://forums.nexusmods.com/index.php?/topic/10149943-help-with-troop-formations/page-2

; leader  = Commander
; soldier = SoldierToObtainRotationSpeed

; =1= init angles
    float aZ  = leader.GetAngleZ()        ; aZ  = CommanderAngleZ
    float aZH = aZH + 180                 ; aZH = CommanderAngleZHalfway
    float L   = aZ - 90.0                 ; L   = LeftBound  - ViewConeHalved
    float R   = aZ + 90.0                 ; R   = RightBound + ViewConeHalved

;    bound-L -90° ... aZ ... +90° bound-R

;          90°
;           |
;        q2 | q1
;  180° --------- 0° = 360°
;        q3 | q4
;           |
;         270°


; =2= normalize angles
    IF (L < 0)
        L = L + 360
    ENDIF
 
    IF (R >= 360)
        R = R - 360
    ENDIF

    IF ((aZH + 180) >= 360)
        aZH = aZH - 180
    ENDIF

    float m = soldier.GetAngleZ()        ;
    float fs                            ; fs  = SpeedToRotate = 5.0

; =3= calculate soldier speed to keep offset to leader
    IF (L < R)                            ; aZ >= 90° /and/ aZ <= 270°

        IF (m < L) || (m > R)
            IF (m < aZH) && (m > R)
                fs = -5.0
            ELSE
                fs = 5.0
            ENDIF
        ELSE
                fs = 0.0
        ENDIF

    ELSE ;IF (L > R)                    ; aZ < 90° /or/ aZ > 270°

        IF (m < L) && (m > R)
            IF (m < aZH)
                fs = -50.0
            ELSE
                fs = 50.0
            ENDIF
        ELSE
                fs = 0.0
        ENDIF

    ENDIF

    RETURN fs
ENDFUNCTION

 

 

 

version 2

 

;------------------------------------------------------------
Float FUNCTION GetRotationSpeed2(Actor leader, Actor soldier)
;------------------------------------------------------------
; https://forums.nexusmods.com/index.php?/topic/10149943-help-with-troop-formations/page-2

; leader  = Commander
; soldier = SoldierToObtainRotationSpeed

; =1= init angles
    float aZ  = leader.GetAngleZ()        ; aZ  = CommanderAngleZ
    float L   = aZ - 90.0                 ; L   = LeftBound  - ViewConeHalved
    float R   = aZ + 90.0                 ; R   = RightBound + ViewConeHalved

;          90°
;           |
;        q2 | q1
;  180° --------- 0° = 360°
;        q3 | q4
;           |
;         270°


; =2= normalize angles
    IF (L < 0)
        L = L + 360
    ENDIF
 
    IF (R >= 360)
        R = R - 360
    ENDIF

    float m = soldier.GetAngleZ()        ;
    float fs                            ; fs  = SpeedToRotate = 5.0

; =3= calculate soldier speed to keep offset to leader
    IF (aZ >= 90) && (aZ <= 270)        ; (L < R)

        IF (m < L) || (m > R)
            fs = 5.0
        ELSE
            fs = 0.0
        ENDIF

    ELSE ;IF (aZ < 90) || (aZ > 270)    ; (L > R)

        IF (m < L) && (m > R)
            fs = 50.0
        ELSE
            fs = 0.0
        ENDIF

    ENDIF

    IF ( fs )            ; IF (fs != 0.0)
        aZ = aZ + 180
        IF (az >= 360)
            aZ = aZ - 360
        ENDIF
        IF (m < aZ)
            fs = -fs
        ENDIF
    ENDIF

    RETURN fs
ENDFUNCTION

 

 

 

version 3

 

;--------------------------------------------------
Float FUNCTION GetRotationSpeed3(Float aZ, Float m)
;--------------------------------------------------
; https://forums.nexusmods.com/index.php?/topic/10149943-help-with-troop-formations/page-2

; aZ = azLeader  = Commander.GetAngleZ()
; m  = azSoldier = SoldierToObtainRotationSpeed.GetAngleZ()

; =1= init angles
    float L   = aZ - 90.0                 ; L   = LeftBound  - ViewConeHalved
    float R   = aZ + 90.0                 ; R   = RightBound + ViewConeHalved

;          90°
;           |
;        q2 | q1
;  180° --------- 0° = 360°
;        q3 | q4
;           |
;         270°


; =2= normalize angles
    IF (L < 0)
        L = L + 360
    ENDIF
 
    IF (R >= 360)
        R = R - 360
    ENDIF


; =3= calculate soldier speed to keep offset to leader
    IF (aZ >= 90) && (aZ <= 270)        ; (L < R)

        IF (m < L) || (m > R)
            L = m                ; save value
            m = 5.0                ; SpeedToRotate = 5.0
        ELSE
            m = 0.0
        ENDIF

    ELSE ;IF (aZ < 90) || (aZ > 270)    ; (L > R)

        IF (m < L) && (m > R)
            L = m                ; save value
            m = 50.0
        ELSE
            m = 0.0
        ENDIF

    ENDIF

; =4= calculate for negative speed
    IF ( m )                ; IF (m != 0.0)
        aZ = aZ + 180
        IF (az >= 360)
            aZ = aZ - 360
        ENDIF
        IF (L < aZ)                ; "L" has value of function parameter "m"
            m = -m
        ENDIF
    ENDIF

    RETURN m
ENDFUNCTION

 

 

 

you should also thinking about to use the native function GetHeadingAngle(), https://www.creationkit.com/index.php?title=GetHeadingAngle_-_ObjectReference

 

 

; Assume a view from above:
;
;              C
;              |
;              |
;              |
;            , A------->  (As heading [A is facing this direction])
;        , '    `
;    , '          `
;  D                `
;                     `
;                       `
;                         B

    float fAngle = A.GetHeadingAngle(C) ; fAngle will equal -90.0

; next figure is nearly the same as above, but showed with rotation left by 90°
;
;           ^ 0°      45°
;           |       ,
;       q3  |  q1 ,
;           |   ,
; -90°        ,       90°
;   C ----- A ----- B
;   
;           |
;       q4  |  q2
;           |
;
;           D           
;     -180° = 180°

 

 

 

(4) as maxarturo wrote: Optimization is "do your things with the least possible amount of system resources."

Edited by ReDragon2013
Link to comment
Share on other sites

 

I'm guessing that you are referring to this post:
There are a lot of things that are wrong here, but i will just meantion the most importants.
1) You should always take in account that players won't just have your mod, but they could have hundreds of mods, with hundreds of heavy scripted quests, abilities, scenes, weapons, spells, followers.... etc.
Which is a strong reason that someone would use a combat/war mod like yours to test/see their stuff in action.
For example: I would play your mod just to see my X-Beyond droid, which carries a 1200+ line combat script, decimate an entire army all by her self, and to see repeatedly some of her cool abilities which fire only under certain conditions and when she is in a critical state.
2) You are doing something that the game engine is not made for, and you are dpoing it on a large scale. Just because something can be done it doesn't mean that it safe to do it.
Don't you think that over the 10 years of Skyrim life a lot of experienced modders had the idea to do something similar in the past, but they were never further developed, there must be a reason behind it...
I personally have removed from my mod a large combat scene like this for a bunch of reasons, but mainly for the 'No Consistency' on all systems.
3) You are pulling every 1 sec, and you have a large amount of scripts doing the same thing.
You are giving the system a reason to have a stroke...
4) And a bunch of other things...
Your mod is becoming a recipe for ctds, freezes and corruption.
All "Register" functions have the drawback that can corrupt the save file under certain conditions.
The optimized way to do this is to have 1 "Master Controller" script to handle your troops formation + script pulling, this means to have only 1 script 'Registering' instead of a 100, this does have the disadvantage that there will be different actors responses according to the amount of actors and the system the script is in + the amount of other active scripts at that given point (weaker sys vs strong sys), but it's a million times safer, more stable and reliable than to have a bunch of individual scripts firing.
And do all this in "targeted combat scenes" and to not exaggerate beyond what the game engine is build for, taking always in account the consistency of "Scene's Execution" in ALL systems not only on your system with your 'load order', but on ALL PCs with ALL possible mods set ups.
You need to always have in the back of your mind the word "Optimization", and do your things with the least possible amount of system resources.

 

 

When you mean one "Master Controller", do you mean creating one quest with it's own Reference Aliases that will pair matching actors into the script and thereby eliminating the need for adding thousands of actor properties? So lets say you come across 50 actors in combat, the script will automatically fill X number of those actors into the pool of reference aliases given certain conditionings, and the script will run? Then when combat is over, we could reset those reference aliases that could be filled with different actors for the next combat scene?

 

I'll also look into using events than update, like combat state changed, OnHit, or when an actor has started an attack (can't recall the name of this event).

 

So I am guessing that the issue here is the sheer amount of actors that the script has to run through along with registering every second will cause problems, so the solution is to find a way to make the same outcome occur just with a script that is flexible and can determine which actors to use on the spot rather than going through a large preselected list of properties and finding an alternative event compared to OnUpdate()?

 

 

If I recall during City Sieges in the vanilla game, it has dozens of reference aliases relating to citizens (so they had citizen1, citizen2, citizen3...citizen 40, etc.) which they would detect, fill, and disable until the siege was over. So would it be a good idea to follow something similar, except using soldiers as opposed to citizens and a formation script instead of disable().

Edited by Guest
Link to comment
Share on other sites

The 'Master Controller' logic / script is called the script that autonomously and exclusively handles a large amount of scripts, something like a 'Merged' script, for example:

Let's say you have 5 puzzles that each one reacts to anyother puzzle that's been triggered, in this circumstance you will have 5 scripts firing at the same time, by creating 1 'Master Controller' for all 5 puzzles, everything gets executed in order without causing system chaos and avoiding the infamous "Jumping / Skipping Functions" when the system is too busy, this is a fail safe of the game engine to avoid ctd when it's overloaded.


In your case the 'Master Controller' would be either a script on the platoon's commander or a quest as you described, that will handle and execute everything in order.


Not knowing the exact setup of your mod and / or your idea, i can't provide an accurate method for doing it, but from the little i know about your idea as a whole, i would go with the 'Quest Monitoring' approach, and i would be sure that no more than 2 quest are active at any given time, no more than 2 enemy platoons in combat at the same time.


You still need to do a lot of testing to find the sweet spot of numbers of npcs that can be active without causing issues like frozen npcs and many others, so if you find that the sweet spot is, let's say 50 npcs, then you do it with 40.


Have a happy modding.

Edited by maxarturo
Link to comment
Share on other sites

So could another option be to create a master controller quest which detects certain commander NPCs that are loaded around the player, and when it detects those NPCs, it starts another quest associated with that platoon of soldiers with its own scripts, and that quest and script will stop should the commander NPC is unloaded and not around the player anymore? That way it will only load certain scripts that it has to and only has the commander and quest properties? Edited by Guest
Link to comment
Share on other sites

AHA!

I got it to finally work well! Now every single large patrol I come across (16 men - 22 men) now goes into combat formations and fights smart and intelligently, without any issues with scripts and latency issues.

 

I did exactly what was described above. I made a master controller script which detected the commander, and once they were detected, their respective quest would start, triggering the rest of the formation and AI scripting, and their respective quest/script would end once they were un-loaded from the cell. This is much more efficient than having everything evaluated at once.

 

Thanks to everyone who has helped make this a possibility, people had been wondering if battle formations could be possible in Skyrim and now it has become a reality!

Edited by Guest
Link to comment
Share on other sites

Yeap, you read exactly my mind...!!!, this is 1 of the optimized way to do this, but i would recommend you to do a lot of testings under different scenarios, because you will find 'hiccups' (it's unavoidable), especially if you are using events like OnLoad / Unload, OnCellAttach / Dettach, OnAttachedToCell / OnDetachedFromCell, OnCombatStateChanged and many other.


All of those events have at least 1 flaw that can easily F*** UP rapidly everything !!!, not to mention all the undocumented flaws that all papyrus functions have, that you can only discover after a lot of testing / experimantation under different cenarios, which will lead you to start developing "Fail Safes" to ensure stability and consistency.

* The development of script's "Fail Safes" are absolutely necessary due to the unreliable nature of the game engine and coding language.


* You wouldn't believe how many worms are hiding beneath a lot of papyrus functions.


Don't ever take this game engine for granted, it's not by far the most reliable engine out there... and you can easily be dragged into a state of comfortness and reassurance just by seeing your stuff working, and later on discover that your stuff has flaws that are not really caused by you, but by the game itself...


Congrats and have a happy modding.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...