Jump to content

[IV|C#] Scripting Tutorial voor Beginners


Hanneswasco

Recommended Posts

GTA IV: Scripting Tutorial in C#

Gelieve hier niet te reageren. Bij vragen, opmerkingen of complimenten (:engel:) kan je terecht in het reactietopic.

Inhoud

  1. Benodigdheden
  2. Een eerste script
  3. Een auto spawnen
  4. Jezelf een wapen geven
  5. ?? Suggesties kunnen in het reactietopic terecht. ??

Benodigdheden

  • Grand Theft Auto IV (PC-versie)
  • Asi loader of een alternatief: bijvoorbeeld XLiveLess
  • GTA IV .NET Scripthook
    • Om in GTA IV te kunnen scripten maken we gebruik van de
    .NET Scripthook. Ik heb hem zelf even geüpload zodat je hem ook kan downloaden mocht de downloadlink daar niet meer werken:
    scripthookdotnet.zip
    Pak dit archief uit in je GTA IV map (standaard te vinden in C:\Program Files\Rockstar Games\Grand Theft Auto IV). De bedoeling is dat ScriptHook.dll en ScriptHookDotNet.asi in de GTA IV map terecht komen en dat er een nieuwe map aangemaakt werd genaamd scripts. In deze map zullen wij onze toekomstige scripts plaatsen. De map scripts bevat ook nog een map for Developers, welke op zijn beurt enkele voorbeeldscripts bevat én belangrijk: de ScriptHookDotNet.dll in de submap bin. Deze DLL zullen we straks nodig hebben om zelf scripts te schrijven!
    Het bestand ScriptHookDotNet.asi dat in je GTA IV map staat zal enkel werken als je een Asi loader of alternatief geïnstalleerd hebt!

[*]Visual Studio

  • In deze tutorial zal ik Visual Studio Professional 2012 gebruiken, de Express-versie daarvan is gratis te downloaden op de
website van Microsoft. Als je student bent kan je via de Microsoft DreamSpark-website de Professional-versie gratis downloaden, maar voor hetgeen ik je hier zal leren voldoet de Express-versie.
Na het downloaden en installeren van Visual Studio zal je bij het eerste maal opstarten de vraag krijgen in welk soort omgeving (environment) je wilt werken: selecteer hier Visual C# Development Settings. Als je de vraag niet gekregen hebt, of je hebt het programma al eerder geïnstalleerd dan kan je dit nog altijd aanpassen door naar Tools -> Import and Export Settings... te gaan.

[*]Bij voorkeur ervaring in het programmeren (van C#), indien je dit niet hebt lees je best deze tutorials even door.

[*]Je gezond verstand

Een eerste script


  • Een project aanmaken

    • Na het opstarten van Visual Studio kan je, zoals te verwachten, een nieuw project aanmaken door naar File -> New -> Project... te gaan (sneltoets Ctrl + Shift + N). Hierna zal er een venster geopend worden. Selecteer hier Class Library en geef een gepaste naam aan het project (in mijn geval is dit "IVScriptingTutorial"). Klik daarna op OK.
NewProject-Small_zpsc672a602.png
Als alles goed gegaan is zou je nu in de Solution Explorer het project moeten zien dat je net hebt aangemaakt, met al een eerste klasse-bestand genaamd Class1.cs. Dit bestand zullen we straks hernoemen en aanvullen met code. Maar eerst moeten we nog enkele aanpassingen aan het project maken, we moeten namelijk nog een referentie toevoegen naar het DLL-bestand dat bij de .NET Scripthook zat.

Referenties toevoegen


  • Klik met je rechtermuisknop op je project in de Solution Explorer en klik op Add Reference... (of klik in de menubalk op Project -> Add Reference...). Er zal een nieuw venster, de Reference Manager, verschijnen, ga hier naar Browse en klik rechtsonder op de knop Browse..., zoek de ScriptHookDotNet.dll (normaal in C:\Program Files\Rockstar Games\Grand Theft Auto IV\scripts\for Developers\bin) en selecteer deze.
ReferenceBrowse-Small_zpsd71ac55a.png
Verder zullen we ook nog andere referenties toevoegen die standaard in het .NET framework zitten. Ga in de Reference Manager naar Assemblies -> Framework (als je dat niet ziet, zoek dan naar .NET) en zorg dat de checkboxen van System.Drawing en System.Windows.Forms aangevinkt zijn.
ReferenceFramework-Small_zpsdb7a3e8b.png
Alle configuraties van het project zijn nu gedaan, nu zullen we een héél kort script schrijven om te testen of alles werkt!

Hallo GTA IV!


  • Open het bestand Class1.cs, als dit nog niet geopend is. Bovenaan het bestand staan er verschillende regels die de syntax using Xxx.Yyy.Zzz; hebben. Dit geeft aan dat we een bepaalde referentie willen gebruiken. Breidt dit uit met onderstaande regels, om aan te geven dat we de referenties, die we daarnet aan het project toegevoegd hebben, in dit bestand willen gebruiken. Deze referenties bevatten verschillende functies, klassen en andere dingen die erg handig zijn om te gebruiken.
using System.Windows.Forms;
using System.Drawing;
using GTA;


In principe zijn deze 3 using regels voldoende, en kunnen we degene die er al stonden verwijderen, aangezien we die niet gaan gebruiken. Onder de using-regels zien we het volgende staan:

namespace IVScriptingTutorial
{
   public class Class1
   {
   }
}


Eerst zeggen we dat we in de namespace van het project willen werken: dit wilt zeggen dat alles wat we tussen de accolades {} hierna schrijven bereikbaar is vanuit het project IVScriptingTutorial. Dit lijkt nu nogal vanzelfsprekend, maar als we later meerdere bestanden gaan aanmaken moeten we zeker zijn dat we vanuit één bestand aan aangemaakte klassen uit een ander bestand kunnen opvragen.
Daarna definiëren we een klasse genaamd Class1 (je mag gerust deze naam veranderen). Als je niet weet wat een klasse is kan je de tutorial die ik in het begin van de tutorial gegeven hebt even doorlezen, maar voor nu is het nog niet zo belangrijk wat een klasse precies is. In deze klassen zullen we ons eerste script schrijven, maar om dat te doen moeten we zeker zijn dat de klasse 'opgestart' wordt. Met de .NET Scripthook kan dit heel gemakkelijk door de klasse te laten overerven van de klasse Script: dit doe je door achter de klasse definitie : Script te schrijven.

public class Class1 : Script
{
}


Als een object van deze klasse aangemaakt wordt gebeurt er nu nog niets, want we hebben nog niets tussen deze accolades geschreven. We zullen eerst een constructor aanmaken, dit is een blok code die automatisch wordt uitgevoerd bij het aanmaken van een object van een bepaalde klasse. Wat een object en klasse precies is, zal ik in later gedeelte uitleggen.
Een constructor heeft altijd dezelfde naam als de klasse, in dit geval dus Class1:

public class Class1 : Script
{
   public Class1()
   {
       // Hier komt de code
   }
}


Wat we nu gaan doen is een methode aanmaken, waarvan we willen dat deze bij het tekenen van elk frame uitgevoerd wordt. We noemen deze methode bijvoorbeeld tekenElkFrame. Deze methode aanmaken gebeurt op een ongeveer gelijke manier als het aanmaken van de constructor. Verder staan er tussen de haakjes nu 2 parameters. Wat die zijn en doen leg ik later uit.

public class Class1 : Script
{
   public Class1()
   {
       // Hier komt de code
   }

   public void tekenElkFrame(object sender, GraphicsEventArgs e)
   {
       e.Graphics.DrawText("Hallo GTA IV!", 10, 20);
   }
}


Zoals je ziet is de methode tekenElkFrame al ingevuld met een regel code die echt iets doet. Deze regel tekent de tekst "Halllo GTA IV!" op het scherm, op positie (10, 20). Dat wil zeggen, de 10e pixel van links en de 20e pixel van boven.
Gewoon de methode aanmaken is nog niet genoeg: deze wordt nu nog nergens uitgevoerd. In de constructor zullen we deze methode meegeven als handler en zullen we zeggen dat deze moet uitgevoerd worden bij het tekenen van elk frame.

public class Class1 : Script
{
   public Class1()
   {
       this.PerFrameDrawing += new GraphicsEventHandler(tekenElkFrame);
   }

   public void tekenElkFrame(object sender, GraphicsEventArgs e)
   {
       e.Graphics.DrawText("Hallo GTA IV!", 10, 20);
   }
}


Ons eerste script is nu af! Nu moeten we het nog omvormen tot een juist bestand zodat het herkent kan worden door de Scripthook in GTA IV.

Omvormen tot één .net.dll-bestand


  • Erg moeilijk is deze stap niet, maar wel cruciaal als je je script wil uitproberen. In de menubalk klik je op Build -> Build Solution (sneltoets F6). Als alles goed is gegaan zou je onderaan het venster de melding Build succeeded moeten zien staan. Als hier Build failed staat dan is er ergens iets mis in je script. In dat geval moet je even kijken naar de Error list en de errors verbeteren (waarschuwingen hoef je je niets van aan te trekken). Als je de fout niet vindt dan moet je je script hier maar eens posten samen met de errors die je krijgt.
    Nu zal een bestand NaamVanJeProject.dll aangemaakt worden in de Debug-map van je project. Standaard vind je dit hier: Documents\Visual Studio 2012\Projects\<NAAM SOLUTION>\<NAAM PROJECT>\bin\Debug. Als het niet in de Debug map staat, dan waarschijnlijk in de Release map. Je kopieert dit .dll-bestand naar de scripts map in je GTA IV installatiemap en hernoemt dit naar NaamVanJeProject.net.dll.
    Nu zou je script moeten werken. Als je GTA IV nu opstart zal je de tekst "Hallo GTA IV!" te zien krijgen in je linkerbovenhoek, zoals op onderstaande screenshot.
HalloGTAIV-Small_zps9d1cc379.png
Voor de volledigheid upload ik hier nog even het class-bestand en de .net.dll:
Class1.zip
IVScriptingTutorial.net.zip

Link to comment
Share on other sites

Een auto spawnen


  • Introductie

    • In deze tutorial zal ik je leren hoe je een auto tevoorschijn tovert en hoe je er aanpassingen aanmaakt.
      We vertrekken vanuit een nieuw project, met uiteraard de juiste references toegevoegd, zoals uitgelegd in de vorige tutorial. Je kan hiervoor alle stappen hernemen, of je kan het Class1.cs bestand gewoon verwijderen/aanpassen uit ons vorige project.
      We maken een nieuw klasse-bestand, dit doen we door in de menubalk op Project -> Add Class... (sneltoets Shift + Alt + C) te klikken. Daar selecteer je Class en geef je een gepaste naam. Ik ga het "AutoScript.cs" noemen.
      In ons nieuwe bestand doen we soortgelijke aanpassingen als in de vorige tutorial. We voegen bovenaan de drie using-regels toe en laten de klasse AutoScript overerven van Script. Dan krijgen we een script zoals hieronder.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using GTA;

namespace IVScriptingTutorial
{
   class AutoScript : Script
   {

   }
}


De methode spawnInfernus()


  • We beginnen met een methode aan te maken genaamd spawnInfernus().
    Intermezzo: wat is een methode? + wat is commentaar?
Een methode is een stukje code dat wordt uitgevoerd wanneer we deze vanuit een andere plaats in de code oproepen. Na het uitvoeren van dit blokje code keren we terug naar de plaats waar we de methode hebben opgeroepen.
Wij zullen de methode spawnInfernus() aanmaken. In de constructor kunnen we deze methode bijvoorbeeld oproepen.
class AutoScript : Script
{

   public AutoScript() // constructor
   {
       // doe iets
       spawnInfernus();
       // doe iets anders
   }

   public void spawnInfernus()
   {
       // spawn een Infernus
   }
}


In dit voorbeeld zien we dat in de constructor AutoScript() de methode spawnInfernus() wordt opgeroepen. We weten dat de constructor van een klasse die overerft van Script automatisch bij het opstarten van het spel uitgevoerd wordt. Eerst zal dus // doe iets uitgevoerd worden. Daarna zal de methode spawnInfernus() opgeroepen worden, en dus // spawn een Infernus. Nadat de methode spawnInfernus() gedaan is zal // doe iets anders uitgevoerd worden.
Uiteraard zullen // doe iets, // spawn een Infernus en // doe iets anders niets doen. Dit zijn commentaar-regels, kenmerkend aan de twee schuine strepen //. Deze doen niets in het script en worden genegeerd als het script uitgevoerd wordt. Ze zijn er speciaal voor de programmeur zodat die commentaar kan toevoegen: bijvoorbeeld om uit te leggen wat er in een bepaald stukje code gebeurt.

In de methode spawnInfernus() zullen wij, jawel, een Infernus doen verschijnen.
Een voertuig doen verschijnen is niet moeilijk, je doet het met de methode World.CreateVehicle(...). Deze methode vraagt 2 argumenten: een model van het voertuig en een positie waar het voertuig moet verschijnen. Deze twee argumenten zijn heel makkelijk aan te maken of te verkrijgen:
   public void spawnInfernus()
   {
       // spawn een Infernus
       Model modelInfernus = new Model("INFERNUS");
       Vector3 positie = Player.Character.Position;

       Vehicle infernus = World.CreateVehicle(modelInfernus, positie);

       Game.DisplayText("Infernus gespawnt!", 2000);
   }


Eerst maken we een model aan, simpelweg door new Model("MODEL_NAAM") op te roepen. MODEL_NAAM is meestal de naam van het voertuig zelf, op sommige uitzonderingen na.
Daarna vragen we de positie van de speler op. Dit spreekt redelijk voor zich: we vragen de speler op via Player, hieruit vragen we de Character op (door een punt ertussen). En daaruit vragen we de Position op. Dit is een Vector3, welke een x-, y- en z-coördinaat bevat, en dus een bepaalde positie (lengte, breedte, hoogte) op de map beschrijft.
Nu we een model en positie hebben kunnen we een voertuig of Vehicle aanmaken met de World.CreateVehicle()-methode.
Als extaatje roepen we daarna de methode Game.DisplayText() op. Deze zal de tekst "Infernus gespawnt!" voor 2 seconden (= 2000 miliseconden) laten zien in de linkerbovenhoek van het scherm.

De methode binden aan een toetsaanslag


  • We hebben nu een methode spawnInfernus(), maar deze wordt nog nergens opgeroepen. We kunnen, zoals in het Intermezzo, deze methode plaatsen in de constructor, maar dan zou de Infernus spawnen in de safehouse van Niko, en dat is ook niet de bedoeling. Ideaal zou zijn dat de methode opgeroepen wordt bij het drukken op een toets, bijvoorbeeld F1. Dit kan heel gemakkelijk via de methode BindKey().
    In de constructor roepen we deze methode op en geven daaraan de toets en methode mee. Dan komen we aan zo'n constructor:
   public AutoScript() // constructor
   {
       BindKey(Keys.F1, new KeyPressDelegate(spawnInfernus));
   }


Keys.XX staat voor de toets XX, waarbij XX vrij te bepalen is. Zo staat F1 staat uiteraard voor F1, T staat voor de letter T en NumPad0 staat voor de 0 op het numerieke gedeelte (helemaal rechts) op je toetsenbord. Van het tweede stukje hoef je je niet te veel aan te trekken. Weet gewoon dat je in de plaats van spawnInfernus ook een andere methode kan meegeven.
Als we dit script nu builden, de .dll verplaatsen en hernoemen, het spel opstarten en op F1 duwen zal er een Infernus verschijnen en de tekst "Infernus gespawnt!" te zien zijn!
InfernusGespawnt-Small_zpsb586238a.png

Voor de volledigheid hieronder nog eens het klasse-bestand AutoScript.cs en de .net.dll:

AutoScript.zip

IVScriptingTutorial.net.zip

Link to comment
Share on other sites

Jezelf een wapen geven


  • Introductie

    • In deze tutorial zullen we de speler een wapen geven en zijn health bijvoegen. De eerste stappen zijn gelijk aan deze uit de vorige tutorial. Maak een nieuw project aan, of vertrek vanuit het voorgaande project. Maak een nieuw klasse-bestand aan, ik noem het PlayerScript.cs. Voeg de nodige using-regels toe en laat de klasse overerven van Script.

De methode geefWapen(String wapenNaam)


  • In de vorige tutorial hebben we de methode spawnInfernus() gemaakt. Deze methode kon één ding: een Infernus spawnen. Als we een een andere auto wouden spawnen, bijvoorbeeld een Sabre, dan moesten we een heel nieuwe methode maken genaamd spawnSabre(). De inhoud van de methode spawnSabre() zou eigenlijk identiek zijn aan deze van spawnInfernus() op stukje na. Waar "INFERNUS" staat moet "SABRE" staan.
    We zouden een soortgelijke situatie kunnen bekomen bij de methode geefWapen(), maar dit willen we niet. Daarom geven we een argument mee aan de methode, namelijk String wapenNaam, wat ons de methode geefWapen(String wapenNaam) geeft. Dit argument bestaat uit twee delen: het eerste, String, staat voor het type van het argument. Een String is een stukje tekst, dit kan je herkennen doordat het rond aanhalingstekens "" staat. Het tweede deel van het argument, wapenNaam, is gewoon een naam voor het argument. In de methode kunnen we via deze naam het argument gebruiken.
    Bij het oproepen van de methode moeten we niet het argument in 2 delen opsplitsen. geefWapen("PISTOL") is genoeg, het argument moet gewoon van het type String zijn.
    Goed, dat was wat achtergrondinformatie over argumenten in methodes. Nu over na de echte code. De wapens van de speler kan je beheren via Player.Character.Weapons. Via deze WeaponCollection kunnen we de hoeveelheid ammo van elk wapen aanpassen. Als je van een bepaald wapen 0 ammo hebt, dan heb je het wapen dus niet. Ook is er van elk wapen een limiet aan ammo dat je kan hebben, maar je kan dit limiet verhogen, maar dat is iets voor later. Als je bijvoorbeeld van je SMG 100 ammo wilt hebben doe je dit via de instructie:
Player.Character.Weapons.FromType(Weapon.SMG_MP5).Ammo = 100;


Wil je een ander wapen, dan moet je simpelweg 'SMG_MP5' in de instructie veranderen naar het passende wapen. Wil je meer of minder ammo dan verander je '100' naar het aantal dat jij wilt.
Wat we nu willen is dat het wapen dat we meegeven afhangt van het argument (String wapenNaam). Dit zullen we doen aan de hand van if-elseif-else instructies.

public void geefWapen(String wapenNaam)
{
   Weapon wapen;
   if (wapenNaam == "Pistol")
   {
       wapen = Weapon.Handgun_Glock;
   }
   else if (wapenNaam == "SMG")
   {
       wapen = Weapon.SMG_MP5;
   }
   else
   {
       wapen = Weapon.Unarmed;
   }
   Player.Character.Weapons.FromType(wapen).Ammo = 100;
}


Als je nu geefWapen("PISTOL") oproept zal je een pistol (glock) krijgen met 100 kogels. Roep je geefWapen("SMG") op dan krijg je een SMG (MP5) met 100 kogels. Geef je iets anders dan "PISTOL" of "SMG" mee als argument dan gebeurt er niets, nouja, dan passen we de ammo van Unarmed (= je vuisten) aan, maar dat zul je dus niet merken.
Intermezzo: wat is een if-else instructie?

In het voorbeeld hierboven zeg ik if (wapenNaam == "Pistol"), vertaalt naar het Nederlands als wapenNaam gelijk is aan "Pistol" dan... voeren we het stukje tussen de accolades na de if-instructie uit.
else if (wapenNaam == "SMG") vertalen we: ... anders, als wapenNaam gelijk is aan "SMG" dan... voeren we het stukje tussen de accolades hierna uit.
Hierna heb je nog een algemeen geval else, dat wordt uitgevoerd als de if en alle else-if-instructies niet uitgevoerd werden.
Het voorbeeld kunnen we nu makkelijk uitbreiden voor alle wapens:
public void geefWapen(String wapenNaam)
{
   Weapon wapen;

   if (wapenNaam == "BaseballBat")
   {
       wapen = Weapon.Melee_BaseballBat;
   }
   else if (wapenNaam == "Knife")
   {
       wapen = Weapon.Melee_Knife;
   }
   else if (wapenNaam == "Grenade")
   {
       wapen = Weapon.Thrown_Grenade;
   }
   else if (wapenNaam == "Molotov")
   {
       wapen = Weapon.Thrown_Molotov;
   }
   else if (wapenNaam == "Pistol")
   {
       wapen = Weapon.Handgun_Glock;
   }
   else if (wapenNaam == "CombatPistol")
   {
       wapen = Weapon.Handgun_DesertEagle;
   }
   else if (wapenNaam == "PumpShotgun")
   {
       wapen = Weapon.Shotgun_Basic;
   }
   else if (wapenNaam == "CombatShotgun")
   {
       wapen = Weapon.Shotgun_Baretta;
   }
   else if (wapenNaam == "MicroSMG")
   {
       wapen = Weapon.SMG_Uzi;
   }
   else if (wapenNaam == "SMG")
   {
       wapen = Weapon.SMG_MP5;
   }
   else if (wapenNaam == "AssaultRifle")
   {
       wapen = Weapon.Rifle_AK47;
   }
   else if (wapenNaam == "CarbineRifle")
   {
       wapen = Weapon.Rifle_M4;
   }
   else if (wapenNaam == "SniperRifle")
   {
       wapen = Weapon.SniperRifle_Basic;
   }
   else if (wapenNaam == "CombatSniper")
   {
       wapen = Weapon.SniperRifle_M40A1;
   }
   else if (wapenNaam == "RPG")
   {
       wapen = Weapon.Heavy_RocketLauncher;
   }
   else
   {
       wapen = Weapon.Unarmed;
   }
   Player.Character.Weapons.FromType(wapen).Ammo = 100;
   Game.DisplayText(wapenNaam + " gegeven!");
}


De wapennaam die ik meegeef aan de methode moet dus overeenkomen met de benamingen op de WikiGTA-pagina. Je kan deze zelf ook aanpassen door de namen tussen de aanhalingstekens te veranderen. Verder is er van alle wapens ook een limiet van aantal kogels, zo kan een RPG maximum 8 raketten hebben, en zal in het voorbeeld hierboven de RPG ook maar 8 raketten bevatten, en geen 100.

De KeyDownHandlers


  • We hebben in een vorige tutorial reeds de BindKey-methode gezien, welke een toetsaanslag bindt aan een bepaalde methode. Een andere manier om dit te doen is via een KeyDownHandler. Hiervoor moeten we een nieuwe methode aanmaken (ik noem hem keyDownHandler) met 2 vastgelegde argumenten (object sender, GTA.KeyEventArgs e). Verder moeten we deze handler toevoegen in de constructor. Nu zal deze methode opgeroepen worden als gelijk welke toets ingedrukt wordt.
    We komen aan onderstaande code:
public PlayerScript()
{
   this.KeyDown += new GTA.KeyEventHandler(keyDownHandler);
}

public void keyDownHandler(object sender, GTA.KeyEventArgs e)
{
   // Hier schrijf je de keyDownHandler
}


Uit het argument GTA.KeyEventArgs e kunnen we te weten komen welke toets precies is ingedrukt:

public void keyDownHandler(object sender, GTA.KeyEventArgs e)
{
   if (e.Key == Keys.F3)
   {
       geefWapen("CarbineRifle");
   }
   else if (e.Key == Keys.F4)
   {
       geefWapen("RPG");
   }
}


Via e.Key kunnen we dus te weten komen welke toets (key) is ingedrukt. Als deze in bovenstaande methode F3 is, dan geven we een CarbineRifle, en als die F4 is geven we een RPG.

Resultaat


  • Dit script zou nu in GTA IV moeten werken.
RPGGegevenSmall_zpsb42c2ea7.png
Zoals altijd hier het .cs-bestand en de .net.dll:
PlayerScript.zip
IVScriptingTutorial.net.zip

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
  • Recently Browsing   0 members

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