Grundwissen:
Variabeln, Normales Scriptverständniss, etwas Hazard Sachwissen
Schwierigkeit:
*****
Hallound Herzlich willkommen zu dem 2ten Teil des Tutorials "Hazard Fahrzeuge mit Tank-Physics und Geschütz erstellen".
Dies ist eine Fortsetzung zum
ersten Teil.
In diesem Tutorial wollen wir einem Fahrzeug ein Geschütz geben, von dem man aus dem Innenraum heraus schießen kann.
Dieses Fahrzeug ist heute nichtmehr der Warthog, nein es ist ein Ghost. (Wer Halo kennt, weiß was ich meine).
Hier erstmal die Grafik von diesem Ghost:
So jetzt geht auch direkt los:
Schritt 1: Geschütz DefCore und ActmapAlso ersteinmal kopiert ihr in euer Fahrzeug, in meinem Falle dem Ghost, das Turret.c4d Objekt aus dem Panzer in euer Objekt.
Das befindet sich hier:
Dann öffnet ihr in euerem Kopiertem Turret.c4d Objekt erstmal die Defcore.
Die Änderungen:
- Die ID ändern, in eine noch nicht bereits vergebene.
- Height und Width auf "1" setzen. Das verhindert das die Grafik ini dem Objekt zum Vorschein kommt.
- Die ganzen Timer Calls rausschmeißen.
Als nächstes gleich die ActMap öffnen.
So muss sie aussehen:
- ActMap.txt schrieb:
- [Action]
Name=Attach
Procedure=ATTACH
Directions=2
Length=1
Delay=1
FacetBase=0
Facet=0,0,0,1
NextAction=Attach
StartCall=Timer
Es reicht nur diese Eine Action drinnzuhaben, der Rest kann rausgeschmissen werden.
Nun, ein paar Erklärungen. Wir wollen das "Geschütz" an unserem Fahrzeug mittig drankleben, damit wir zu den Seiten überall den Gleichen Abstand haben. Dazu muss man wissen, wie das mit Attachen funktioniert. Wenn man einfach nur pGeschütz->SetAction("Attach",pFarhzeug); macht, dann hängt es sich an den ERSTEN Vertex an.
Deshalb wichtig: in eurem Fahrzeug muss der erste Vertex genau der Mittelpunkt sein. Also wenn ihr euren Offset in der Mitte platziert habt, dann einfach VertexX=0 und VertexY=0, dann ist der erste Vertex genau in mittig.
Schritt 2: Der Script des GeschützesNun zum wichtigsten bei dem Geschütz. Der Script!
Öffnet ersteinmal den Script in eurem Geschütz, der sollte genauso aussehen wie der vom Originalobjekt. Löscht den gesamten Script das wir von neu anfangen können.
Wir tasten uns jetzt Schritt für Schritt an den Script ran.
Zuersteinmal sollten wir natürlich das WEPN Objekt inkludieren! Gemacht wirds mit
- Code:
-
/*-- Geschütz --*/
#strict
#include WEPN
local pGhost; //Das speichert die Variable zu welchem Ghost dieses Geschütz überhaupt gehört
Als nächstes den / die Schussmodi. Wer schonmal Hazardwaffen gescriptet hat weiß, was für Werte er einsetzen muss. Wer noch nie sowas gemacht empfehle ich die Hazard-Dokumentation und auf "Waffen".
Hier mein Code für meine Waffe:
- Code:
-
public func FMData1(int data)
{
if(data == FM_Name) return("Schnellfeuer");
if(data == FM_AmmoID) return(ENAM);
if(data == FM_AmmoLoad) return(50);
if(data == FM_Reload) return(140);
if(data == FM_Recharge) return(6);
if(data == FM_AmmoUsage) return(1);
if(data == FM_AmmoRate) return(1);
if(data == FM_Auto) return(true);
if(data == FM_Damage) return(12);
return(Default(data));
}
Natürlich könnt ihr die Werte nach eurem Belieben anpassen
Nun kommen wir zur Fire1() Funktion. Da gibts jetzt was zu erklären:
- Code:
-
public func Fire1(object pCaller) // fire the cannon
{
var user = pCaller;
//in meiner Funktion wird auch noch der WINKEL miteinberechnet, weil sich der Ghost auch drehen kann.
var angle; if(GetDir() == DIR_Right)angle=GetR()+90+RandomX(-4,+4); else angle=GetR()-90+RandomX(-4,+4);
//jetzt kommt noch eine weitere Variable rein
var iAbstand=35; //Diese Variable gibt den Abstand von der Mitte aus zur Feueröffnung in Pixel an.
var x,y; if(GetDir() == DIR_Left)x=-iAbstand; else x=+iAbstand; y=-3;
//Schuss erstellen
var ammo = CreateObject(SHT2,x,y,GetController(user));
ammo->Launch(angle,180,400,6,80, 20,0,80); //Abfeuern
// Effekte
if(GetDir() == DIR_Left)MuzzleFlash(30,user,x,y,angle,RGBa(96,64,255,0)); else MuzzleFlash(30,user,30,y,angle,RGBa(96,64,255,0));
// Sound
Sound("Feuersound",0,ammo);
}
Wenn ihr ein Fahrzeug habt das sich nicht dreht könnt ihr das mit den komplizierten Winkelberechnung auch einfach lassen, bei mir war es aber notwendig.
Wichtig ist auch noch die Funktion zum Attachen an das Fahrzeug. Dazu übernehmen wir eine etwas reduzierte Funktion aus dem originalobjekt.
- Code:
-
public func AttachTo(attachto) { // attach to tank
SetDir(GetDir(pGhost));
SetAction("Attach",attachto);
SetR(GetR(attachto));
}
Nochetwas wichtiges: Dem Geschütz immer die gleiche Richtung und Winkel wie dem Fahrzeug geben. Dadurch das wir eine Actino haben die immer wieder "Timer()" aufruft, brauchen keinen DefCore Timer! Und da wir oben bereits eine local "pGhost" definiert haben, in der wir später das Fahrezeug speichern werden, zudem es gehört, nehmen wir einfach sämtliche Winkel und die Richtung und Übertragen sie auf das Geschütz. So:
- Code:
-
private func Timer() {
SetR(GetR(pGhost));
SetDir(GetDir(pGhost));
}
Lange Rede, kurzer Sinn
Jetzt haben wir allen Script zusammen, dass das Geschütz funktioniert. Jetzt müssen wir im Fahrzeug noch etwas tun!Schritt 3: Der Script im FahrzeugUm überhaupt das Geschütz an das Fahrzeug ranzukriegen erstellen wir in der Initialize() des Fahrzeugs das Geschützen und Attachen es mit der Geschütz-eigenen Funktion an dem Fahrzeug.
Außerdem mussen ist es ratsam das Geschütz in einer Lokalen Variable zu speichern.
So gehts:
- Code:
-
#strict
local turret;
func Initialize() { //am Anfang
turret=CreateObject(TRT2);
turret->AttachTo(this());
LocalN("pGhost",turret)=this();
return(1);
}
So, was haben wir gemacht? Wir haben das Object TRT2 (in meinem Fall das veränderte Geschütz) erstellt und im Geschütz die AtachTo Funktion aufgerufen, als Paramater this(), also das Fahreug. Dann wird dem Geschütz noch per LocalN() die pGhost local geändert, nähmlich kriegt sie den Wert this(), also wieder das Fahrzeug. Das ist dazu da, dass das mit dem Richtung und Winkel übertragen auch funktioniert.
Und jetzt kommt das Entscheidene:Wenn wir nun im Object [Werfen] drücken, wollen wir natürlich das unser Objekt auch schießt!
Also machen wir eine ControlThrow() Funktion:
- Code:
-
func ContainedThrow(object driver) {
turret->ControlThrow(driver);
return(1);
}
Wenn ihr das mal testet, schießt euer Geschütz zwar, aber ein HUD oder ne Anzeige ist nirgendswo zu sehen.
Jetzt kommen die Speziellen Funktion für das HUD:
- Code:
-
public func ReadyToFire() { return(1); }
public func UpdateCharge(object driver) {
if(!turret) return();
var hud = driver->GetHUD();
if(hud) {
hud->Update(turret, driver->AmmoStoring(),driver);
}
return(1);
}
Nun was machen wir da? Erstmal legen wir fest, das das Objekt überhaupt "Ready" fürs Schießen ist. (Wenn man diese Funktion weglasst würde jeder Aufruf "0" ergeben, was bedeutet, dass das Geschütz nicht schießen kann)
Als erstes speichern wir das HUD in einer Variable. Das geht über eine Funktion im Hazard. Dann nehmen wir HUD, und rufen Update() auf, mit den Parametern "turret" als Schießendes Objekt, dann der Munitionskapazität des Fahrers und dann nochmal dem Fahrer als Schießenden.
Das ist der ganze Trick dabei!
ABER HALT, das wars noch nicht! Ich wäre nicht Gamer wüsste ich nicht noch ein paar andere HUD Tricks.
Wie wäre es z.B wenn ihr mehrere Feuermodi habt? Dann könnt ihr ja garnicht zwischen ihnen wechseln, weils keine Doppelgraben Funktion gibt!
Das ändern wir aber schleunigst.
- Code:
-
public func ContainedDigDouble(object driver) { // Dig Double (fire mode)
[Feuermodus]
turret->ControlDigDouble(driver);
return(true);
}
Dadurch wird ganz einfach im Geschütz DigDouble() aufgerufen. (Ist durch #include WEPN im Objekt definiert)
ENDE
Und als Endscreenshot mein schießender Ghost:
Die Mühe lohnt sich wirklich!