武装突袭3

武装突袭3

35 个评价
Scripting Working - Lesson 2
由 ffredyk [CZ] 制作
In this lesson, I will try to teach you more advanced stuff than in the first lesson (Scripting Introduction).

I will tell you what multiplayer locality means, how scripts behave in multiplayer, how to create clean and optimized code, what are the functions and how to use them properly. I will show you some of the tricks I figured out and some interesting BIS functions which may give your scenarios more gameplay value.

I am sure you have already tried some basic scripting by yourself, so I won't tell you anything too basic, as it is up to you to figure out most of the scripting or you wouldn't learn nothing.
   
奖励
收藏
已收藏
取消收藏
Functions
Functions are blocks of code saved in the variable for later use.

You can insert whole files of code into functions and that is exactly how function framework system works in Arma 3. But that's more advanced stuff I won't tell you about much more now.

func_basicFunction = { //This is the basic function //You can do whatever you like in here. player sideChat "I was manipulated by basic function to say this text! Help me!"; sleep 5; player sideChat "Function still controls me! What should I do?!"; _obj = [_this,0,objNull,[objNull]] call BIS_fnc_param; if(!isNull _obj && alive _obj) then { _obj sideChat "Kill urself mate.. Theres nothing u can do"; }; //Function can receive parameters, which can be then used inside the function. //Functions can technically receive only one parameter, but if that parameter is an array, you can have additional parameters inside that array. //BIS functions works like this too. And there is even BIS_fnc_param function to help organize array structure of param. You'll learn more about it in Tricks section of this tutorial. true; //I will tell you what this is later }

Our basic function won't do anything on it's own. We need to either call it, or spawn it.

Calling
Calling a function means that the script that called the function will execute it, and then wait's for the function to respond. Function can respond by either completing itself, or by returning variable.

//Code block A _result = [player2] call func_basicFunction; //Code block B

In the example above we see two code blocks. Code block A is executed normally, but code block B waits for basicFunction to complete itself and because we have sleep statement in there, function will wait 5 seconds before executing the rest of the code. So will the code block B, because it waits for the function to complete.

Calling the function also means, that the code which called the function waits for its output, if the function provides any. In this case, variable result will hold boolean value of true because the function gave it as a result.

Spawning
Spawning a function means that the script that spawned the function will give it it's own space and then will carry on on his task.

//Code block A [player2] spawn func_basicFunction; //Code block B

In the example here, code block A executes normaly, then spawning of our basic function occurs and then code block B is immediately executed. Script doesn't wait for function to comlete nor for the output it may give.

You can't receive output from spawned functions, they live in their own world now



If your face is identical, go and try the functions in the editor! :)
Multiplayer / Locality
Multiplayer and it's locality is whole new level on hard difficulty. Everything in multiplayer is local to something. That something is the player, or server. AI units are usually owned by server, and are local to him, this means, that only server can perform specific script commands on them to have effect and nobody else can.

When player plays music, it's only local effect. If you issue:
playMusic "EpicTheme";

It will play music only on those PCs, where the command was issued. This situation can happen when condition needed to play music can be true only on some PCs, and for other not. In this case, only true condition PCs will play music:

if(player distance target < 100) then { playMusic "ClosingIn"; };

Player 1 is 300m away from the target
Player 2 is 70m away from the target
Player 3 is dead

Music will play only for player 2, because player 1 didn't meet the condition rules, and player 3 is dead and didn't die within the 100m range.

If you want to play music for everyone then you need this code:
if({_x distance target < 100} count playableUnits > 0) then { playMusic "EpicTheme"; }

Every PC will now check for all players if they are in the radius for the song to play.
playableUnits is the array which holds all playableUnits players can use, and they can be driven by AI.
If you want only living players be able to launch music you need to modify the radius condition and add isPlayer check


if({_x distance target < 100 && isPlayer _x} count playableUnits > 0) then { playMusic "EpicTheme"; }


You can check if the function you want to use is global or local in the BIS wiki here:
https://community.bistudio.com/wiki/Category:Scripting_Commands_Arma_3

Further reading about locality[community.bistudio.com]

Optimization
Optimization is crucial, because if you make a lot of unoptimized scripts, they will shutter the mission.

You need to think of possible players that will play your scenario, they can have slow PC, they can have bad internet connection, they can be a multitasking crazies, they may want to play on highest FPS possible. And it's your job to make their life easier.

Hordes of scripts
First and basic rule of spawning scripts is:
引用自 Google
Too many cooks spoil the broth
引用自 Google
Enough is enough
etc.

If you spawn 50 scripts, they will all try to execute in one single moment. That's brutal action for the CPU, because it tries to comply to all the scripts which need to get the ♥♥♥♥ done ASAP.

Networking
If you lanch too many MP calls, you may flood the server, or clients. If you issue the order to launch specific function on one client, all clients including the server receives the order, but refuses to complete him, because it is not meant for them to complete it.

If you make order for every piece of client there is, you can end up with 30 orders trying to be issued ASAP. That may lag the synchronization of units, because network will be full of script order, and it can harm the server, or clients' FPS, because it needs some CPU power to be handled.

Busy scripts
If you need to do some hard calculation, be sure your count of script handles is not exceeding CPU core count which may vary. You can perform extreme computations, but leave some space for other scripts and tasks too.
Tips and Tricks
I want to show you some interesting scripts and findings I've made or found in the functions library ingame.
You can enhance your scenario with them, or they might be somehow useful to you.
I have so many scripts I don't even know which are useful and which are not, so I will try to expand this personal library as I find some gold nut inside my folders, or my head produces.

Camera Selfie
Little script I used in my scenario Few bullets as the ending cinematic. It creates camera in front of player, aims at his face and rotates in random direction for few seconds. You can change the script as much as you want and need, it's all yours now.

Note: I didn't think of optimization nor of code readiness, it's ugly and fast-written, bear it in mind when you offence me of amateurism.

func_cameraSelfie = { selfieRunning = true; rlypos = getposvisual playerObj; ("BIS_layerStatic" call BIS_fnc_rscLayer) cutRsc ["RscStatic", "PLAIN"]; waitUntil {!(isNull (uiNamespace getVariable "RscStatic_display"))}; waitUntil {isNull (uiNamespace getVariable "RscStatic_display")}; camlol = "camera" camCreate getpos playerObj; camlol cameraEffect ["internal","back"]; [] spawn { waitUntil { playerObj setpos rlypos; isNil "selfieRunning"; }; }; ranDir = [-20,20]; ran = ranDir select round random 1; campos = [rlypos, 1,(getdir playerObj) + ran] call BIS_fnc_relPos; campos set [2,1.7]; campos2 = ([rlypos, 1,((getdir playerObj + ran) + 180) % 360] call BIS_fnc_relPos); campos2 set [2,1.5]; camlol camPreparePos campos; camlol camPrepareTarget campos2; camlol camCommitPrepared 0; ranDir = ranDir - [ran]; campos = [rlypos, 1,(getdir playerObj) + (ranDir select 0)] call BIS_fnc_relPos; campos set [2,1.7]; campos2 = ([rlypos, 1,((getdir playerObj + (ranDir select 0)) + 180) % 360] call BIS_fnc_relPos); campos2 set [2,1.5]; camlol camPreparePos campos; camlol camPrepareTarget campos2; camlol camCommitPrepared 15; sleep 13; ("BIS_layerStatic" call BIS_fnc_rscLayer) cutRsc ["RscStatic", "PLAIN"]; waitUntil {!(isNull (uiNamespace getVariable "RscStatic_display"))}; waitUntil {isNull (uiNamespace getVariable "RscStatic_display")}; /*if(cameraOn == camlol) then {*/camlol cameraEffect ["terminate","back"];//}; camDestroy camlol; selfieRunning = nil; };

Titles
I was bored while making Anti Air Arcade, and didn't want to search for built-in titles solution from BIS, so I've made, with the little help of BIS functions, my own titles!

func_titles = { _text = [_this, 0, "", [""]] call BIS_fnc_param; //Titles text _time = [_this, 1, 5, [0]] call BIS_fnc_param; //Time the titles are shown [format ["<t align='center' size='0.5'>%1</t>",_text],-1,0.9,_time,0,0,["TITLES"] call bis_fnc_rscLayer] call BIS_fnc_dynamicText; };

GPS Custom video
BIS have already created function for this. But unfortunately, it doesn't work in A3 even it is there though. I have changed it to ugly version which uses window from liveFeed to play OGV videos in it. GPS in Arma 3 doesn't have any frame, so it was ugly to see only video screen without any frame playing on the right side of the screen.

func_GPSVideo = { /* Author: Karel Moricky & modified for A3 by ffredyk Description: Plays in-game video in GPS frame. Parameter(s): _this select 0: STRING - Video Returns: True Usage: ["warning.ogv"] call func_GPSVideo */ #define GPS_DISPLAY (uinamespace getvariable "BIS_RscMissionScreen_customGPSvideo") #define GPS_CONTENT (GPS_DISPLAY displayctrl 1100) #define GPS_FRAME (GPS_DISPLAY displayctrl 1101) [player,player,player] spawn BIS_fnc_liveFeed; _this spawn { _content = _this select 0; _sizeCoef = if (count _this > 1) then {_this select 1} else {1}; (["GPSVideo"] call bis_fnc_rscLayer) cutrsc ["RscMissionScreen","plain"]; waituntil {!isnull (uinamespace getvariable "BIS_RscMissionScreen")}; uinamespace setvariable ["BIS_RscMissionScreen_customGPSvideo",uinamespace getvariable "BIS_RscMissionScreen"]; _contentConfig = configfile >> "RscTitles" >> "RscPIP" >> "controlsBackground" >> "PIP"; _contentX = getnumber (_contentConfig >> "x"); _contentY = getnumber (_contentConfig >> "y"); _contentW = getnumber (_contentConfig >> "w"); _contentH = getnumber (_contentConfig >> "h"); //_contentH = _contentW * (10/16) * (4/3); //--- Aspect ration fix _frameConfig = configfile >> "RscMiniMap" >> "controlsBackground" >> "MiniMap" >> "controls" >> "CA_BackgroundTitle"; _frameX = getnumber (_frameConfig >> "x"); _frameY = getnumber (_frameConfig >> "y"); _frameW = getnumber (_frameConfig >> "w"); _frameH = getnumber (_frameConfig >> "h"); _frame = if (isnil "BIS_fnc_customGPS_Params") then { //--- Default gettext (_frameConfig >> "text"); } else { //--- Modified by BIS_fnc_customGPS _params = BIS_fnc_customGPS_Params; if (count _params > 1) then { _dX = _params select 1; _contentX = _contentX + _dX; _frameX = _frameX + _dX; }; if (count _params > 2) then { _dY = _params select 2; _contentY = _contentY + _dY; _frameY = _frameY + _dY; }; _params select 0; }; _posContent = [ _contentX, _contentY, _contentW * _sizeCoef, _contentH * _sizeCoef ]; _posFrame = [ _frameX, _frameY, _frameW, _frameH ]; //--- Set bis_fnc_customGPSvideo_videoStopped = false; waitUntil {!isNil "BIS_liveFeed"}; BIS_liveFeed cameraEffect ["TERMINATE", "BACK"]; GPS_CONTENT ctrladdeventhandler ["videoStopped","bis_fnc_customGPSvideo_videoStopped = true;"]; GPS_CONTENT ctrlsetposition _posContent; GPS_CONTENT ctrlsettext _content; GPS_CONTENT ctrlcommit 0; GPS_FRAME ctrlsetposition _posFrame; GPS_FRAME ctrlsettext _frame; GPS_FRAME ctrlcommit 0; waituntil {bis_fnc_customGPSvideo_videoStopped}; bis_fnc_customGPSvideo_videoStopped = nil; [] call BIS_fnc_liveFeedTerminate; sleep 1; (["GPSVIDEO"] call bis_fnc_rscLayer) cuttext ["","plain"]; }; };
Finalizing
Thanks for reading this lesson! I hope you learned something and you will be trying to create awesome and fantastic scenarios. I wish you very best of luck and see you in the next lesson!

3 条留言
Elite IV 2015 年 5 月 25 日 下午 10:12 
Wow nice, will use on my Altis Life Sever, Thanks (y)
HellCeaser 2015 年 5 月 20 日 下午 5:05 
<3
oOoOoOoOoOoOoOoOOOooOoOoOOoooo 2015 年 5 月 10 日 下午 3:11 
:FIA: nice keep it up man love it :FIA: