Die Kunst der Kameras

Ich weiß, dass einige unter Ihnen ein paar einfache Kamera Skripts gebrauchen können, hier sind sie. Stellen Sie einfach sicher, dass cameras.wdl eingebunden ist, dann starten Sie Ihr Level und drücken 1...4 um die Sicht zu wechseln: wir haben Vogelperspektive, Seitenansicht, isometrische Ansicht und Marker Ansicht. Die Marker Ansicht ist für eine der „Antennen“ in meinem ersten KI-Demo benutzt worden, und ich dachte, Sie könnten diese in einem Ihrer Projekte nützlich finden.

Ich bin mir nicht sicher ob sie gemerkt haben, dass alle meine Demos alleinstehende Projekte sind, die die Templates nicht verändern - deshalb habe ich die Standardkamera (camera) ausgeschaltet. Ich habe eine neue Kamera mit dem Namen aum_camera erstellt - Deren Parameter werden in dieser Funktion gesetzt:

function set_cameras()
{
      camera.visible = off;
      aum_camera.size_x = screen_size.x;
      aum_camera.size_y = screen_size.y;
      aum_camera.pos_x = 0;
      aum_camera.pos_y = 0;
     aum_camera.visible = on;
     select_camera();
}

Sie können erkennen, dass die Größe der aum_camera direkt von screen_size gelesen wird; daher müssen wir die Bildschirmauflösung nicht wissen. Wir schalten die neue Kamera auf sichtbar und rufen select_camera auf; eine einfache while(1)-Schleife  überprüft, ob wir die Tasten 1...4 gedrückt haben und wechselt entsprechend die Kamera.
 

Vogelperspektive

if (camera_number == 1) 
{
      aum_camera.x = player.x;
      aum_camera.y = player.y;
      aum_camera.z = player.z + 300;
      aum_camera.pan = player.pan;
      aum_camera.tilt = -90;
      aum_camera.roll = 0;
}
 

Die erste Kamera ist wirklich einfach: sie folgt dem Spieler die ganze Zeit, ist über dem Spieler plaziert (aum_camera.z = player.z + 300) und schaut auf den Spieler herab (aum_camera.tilt = -90).
 
 

Seitenansicht
 
if (camera_number == 2)
{
      aum_camera.x = player.x + 200 * sin(player.pan);
      aum_camera.y = player.y - 200 * cos(player.pan) ;
      aum_camera.z = player.z + 10;
      aum_camera.pan = player.pan + 90;
      aum_camera.tilt = 0;
      aum_camera.roll = 0;
}

Diese Kamera benötigt ein paar Trigonometriekenntnisse, daher lasse ich besser ein Bild für mich sprechen:

 
 

Sie können statt 200 auch andere Werte nehmen: kleinere Zahlen lassen die Kamera näher zum Spieler kommen. Ich habe die Kamera ein wenig über den Boden angehoben (player.z + 10) und ich habe sie zum Spieler gedreht (player.pan + 90).
 
 

Isometrische Ansicht

if (camera_number == 3)
{
      aum_camera.x = player.x - 200 * cos(player.pan);
      aum_camera.y = player.y - 200 * sin(player.pan);
      aum_camera.z = player.z + 200;
      aum_camera.pan = player.pan;
      aum_camera.tilt = -30;
      aum_camera.roll = 0;
}

Dies ist Ihre reguläre 3rd Person Kamera. Sie funktioniert gut, hat aber keinerlei Kollisionsabfrage. Falls Sie in einem guten 3rd Person Kamera Skript interessiert sind (die bei Rune benutzte Kamera ist bisher die beste, die ich gesehen hab) schreibe ich vielleicht für eine zukünftige AUM-Ausgabe ein Skript. Hier ein weiteres Bild:
 

Ich hab die Kamera deutlich angehoben, denn diesmal wollte ich sie nach unten schauen lassen (aum_camera.tilt = -30).
 
 

KI Demo (Marker) Ansicht

if (camera_number == 4)
{
      aum_camera.x = player.x + 200 * cos(player.pan) + 150 * sin(player.pan);
      aum_camera.y = player.y + 200 * sin(player.pan) - 150 * cos(player.pan);
      aum_camera.z = player.z;
      aum_camera.pan = player.pan + 130;
      aum_camera.tilt = 0;
      aum_camera.roll = 0;
}
 

Das ist die letzte Kamera; falls Sie gerade auf Ihren Monitor blicken (Ich hoffe es, obwohl ich stolz wäre zu wissen, dass Sie sich AUM4 eingeprägt haben :), diese Kamera würde in der oberen rechten Ecke plaziert sein, dauernd den Spieler ansehen, und sich mit ihm bewegen. Ich habe ein ähnliches Skript für die beiden „Antennen“ in der KI Demo benutzt - hier sind die Bilder:

 

Da Sie nun von den Antennen gehört haben (die Kamera ist plaziert, als wäre sie die vordere rechte Antenne im nächsten Bild), sind Sie bereit für die...
 
 

Erste KI Demo

Erinnern Sie sich an die Standard Patrol-Aktion, welche den Templates beiliegt? Sie mussten einige Positionen im Level plazieren und ihren Pan so anpassen, dass ein virtuell geschlossener Pfad entsteht. Ich mag diese Aktion sehr gerne, aber sie ist etwas zu kompliziert, um sie hier zu erklären, deswegen musste ich sie umschreiben. Ich habe ein wenig KI-Code hinzugefügt und heraus kam FKID, die Demo die mit A4 und A5 funktioniert.

Bevor ich meine Vorgehensweise erkläre, laden Sie bitte die KI Demo in WED und sehen Sie sich das Level an: Es gibt nur 4 Positionen in Nähe der Ecken und eine Menge Kisten, die der Wache im Weg stehen. Kann sie es schaffen? Kann sie allen Hindernissen ausweichen? Warum starten Sie nicht das Level und sehen selbst? Starten Sie aidemo1.wdl.

Ich habe eine Weile zugeschaut (für etwa drei Runden) und bin mit dem Ergebnis immer noch zufrieden. Ich habe eine einfache Idee benutzt: Die Wache hat zwei „Antennen“ die wie folgt palziert werden:

 

 

Sobald sich mindestens eine der Antennen (front_right oder front_left) in einem festen Block befindet (content_solid), meidet die Wache das Hindernis, bis sich die Antenne nicht mehr in einem festen Block befindet.

Sie sollten jetzt das aumai1 Level laden; vergessen Sie nicht, die ai1.wdl Datei zu starten. Bewegen Sie die Blöcke nahe zur Wache und sehen Sie zu wie sie den Block umgeht (falls sie es schafft). Sie können die Antennen sehen - bitte beachten Sie, dass diese nicht benötigt werden, da wir Vektoren benutzen, um den Inhalt zu überprüfen; Ich habe diese sichtbaren Antennen erstellt, um Ihnen zu zeigen wie der Algorithmus funktioniert. Falls Sie die Antennen entfernen wollen, kommentieren Sie einfach folgende Zeilen in ai1.wdl:

create (<marker.mdl>, front_right, frlaction);
create (<marker.mdl>, front_left, frlaction);

Ich habe versucht, anständige, verständliche Variablennamen zu wählen. Falls Sie mehrere „intelligente“ Einheiten verwenden wollen müssen Sie ein paar Variablen zu my.skillxx umbenennen, möglicherweise ihre Werte nach temp kopieren usw.

Bitte beachten Sie, dass sich die folgenden Erklärungen auf ai1.wdl beziehen. Die Funktion init_demo() wird in main aufgerufen und setzt die Position und die Winkel so wie wir sie benötigen:

function init_demo()
{
      camera.x = 0;
      camera.y = 0;
      camera.z = 440;
      camera.tilt = -90;
      camera.pan = 270;
}

Das ganze interessante Zeug ist in der Aktion guard_patrol untergebracht. Als erstes erstellen wir den Block der für die Tests benutzt wird:

block_pos.x = 0;
block_pos.y = 0;
block_pos.z = 0;
create (<block.wmb>, block_pos, move_block); 

Wir plazieren den Würfel (block.wmb) zum Spielstart an den Ursprung; die Aktion die seine Bewegung kontrolliert (move_block) ist wirklich einfach, deswegen werde ich sie hier nicht beschreiben.

Die Funktion create_markers() erstellt die „Antennen“:

function create_markers()
{
 while (1)
 {
       front_right.x = guard_syn.x + 50 * cos(guard_syn.pan) + 20 * sin(guard_syn.pan);
       front_right.y = guard_syn.y + 50 * sin(guard_syn.pan) - 20 * cos(guard_syn.pan);
       front_right.z = guard_syn.z;

       front_left.x = guard_syn.x + 50 * cos(guard_syn.pan) - 20 * sin(guard_syn.pan);
       front_left.y = guard_syn.y + 50 * sin(guard_syn.pan) + 20 * cos(guard_syn.pan);
       front_left.z = guard_syn.z;

       fright_marker = content(front_right);
       fleft_marker = content(front_left);
       if (fright_marker == content_solid)
       {
            obstacle_right = 1;
       }
       else
       {
            obstacle_right = 0;
       }
       if (fleft_marker == content_solid)
       {
            obstacle_left = 1;
       }
       else
       {
            obstacle_left = 0;
       }
       wait (1);
    }
}
 

Bitte vergessen Sie nicht, dass ich vereinfachte Versionen der Funktionen und Aktionen benutze, um Ihnen die Sache zu erleichtern.

Wir erstellen die Kamera Vektoren (front_right und front_left) vor dem Spieler und überprüfen dann ob ihr „Inhalt“ content_solid entspricht. Wenn sich eine der Antennen in einem festen Block befindet, wird die dazugehörige Variable (obstacle_right oder obstacle_left) auf 1 gesetzt.

Hier ist der Rest der guard_patrol-Aktion:

my_pos.x = destination.x - my.x;
my_pos.y = destination.y - my.y;
result = vec_to_angle(my_angle, my_pos);
if (result < 30)
{
      vec_set (my_pos, destination);
      my_angle.pan = my.skill12;
      temp.pan = 60;
      temp.tilt = 90;
      temp.z = 2000;
      scan_pos (my_pos,my_angle,temp);
      if(result > 0)
      {
           vec_set (destination, my_pos);
           my.skill12 = my_angle.pan;
      }
}
my.skill13 = ang (my_angle.pan - my.pan);
if (obstacle_right != 1 && obstacle_left != 1)
{
      my.pan += 0.2 * my.skill13 * time;
}

Die Wache berechnet die Entfernung zwischen ihren Koordinaten und der nächsten Position, dreht sich in diese Richtung und falls sie nah genug ans Ziel kommt (result < 30) wird sie eine neue Position suchen. Falls die Wache eine neue Position findet (result > 0) und sich die Antennen nicht in einem festen Block befinden, wird sie sich zum Ziel drehen. Skill13 enthält den Unterschied zwischen en Winkeln (Pan) der Wache und dem Ziel.

if (obstacle_right == 1)
{
      my.skill30 = my.pan;
      while (my.pan < my.skill30 + 80)
      {
           marker_distance = 0;
           my.pan += 5 * time;
           move_guard();
           wait (1);
      }
}
if (obstacle_left == 1)
{
      my.skill30 = my.pan;
      while (my.pan > my.skill30 - 80)
      {
           marker_distance = 0;
           my.pan -= 5 * time;
           move_guard();
           wait (1);
      }
}
if (obstacle_right == 1 && obstacle_left == 1) {my.pan = random(360);}
if (marker_distance < 50) {marker_distance += 2 * time;}
move_guard();
 

Falls sich eine der Antennen in einem festen Block befindet, speichern wir den Pan der Wache in skill30 und wir fügen 80° mit einer While-Schleife hinzu (oder ziehen 80° ab). Sie können versuchen, kleinere oder größere Pan Werte abhängig von ihren Levels, zu verwenden. Wir müssen marker_distance für kurze Zeit auf 0 setzen um die Erkennung an den Ecken zu verbessern (wenn Sie das Testlevel starten und mit dem Block herumspielen, werden sie verstehen wieso). Falls sich beide Antennen in einem festen Block befinden, benutzt die Wache  einen zufälligen Pan Winkel, der eine der Antennen aus dem festen Block holen wird. Sie wissen dass marker_distance die Entfernung zwischen der Wache und ihren Antennen ist. Während diese Entfernung kleiner als 50 ist, wird sie langsam erhöht.

function move_guard()
{
      move_mode = ignore_you + ignore_passable + activate_trigger;
      ent_move(temp, nullvector);
      ent_cycle("walk",my.skill11);
      my.skill11 += 5 * time; // skill11 = animation frame
      if (my.skill11 >= 100) {my.skill11 = 0;}
}

Diese Funktion bewegt und animiert den Spieler. Ich habe ähnliche Funktionen in Aum2 und Aum3 verwendet, daher wissen Sie wie das funktioniert.

Ich wollte die Antennen anzeigen, daher hab ich zwei Modelle erstellt, die bei den front_right und front_left Vektorkoordinaten plaziert werden

create (<marker.mdl>, front_right, frlaction);
create (<marker.mdl>, front_left, frlaction);

function frlaction()
{
      my.passable = on;
      waitt (2);
      remove me;
}

Die Funktion fr1action erstellt die Modelle für die Antennen, setzt sie auf Passable (damit sie sich in festen Blöcken befinden können), zeigt sie für 1/8 Sekunden an und entfernt die Antennen anschließend.

Ist diese erste KI Demo perfekt? Ich denke nicht; wenn sie perfekt wäre, hätte ich sie Finale KI Demo genannt. Hier sind die Schwachpunkte:
- Der Code funktioniert nicht so toll in engen Bereichen (Sie können marker_distance und marker_width ändern um das Verhalten Ihren Levels anzupassen);
- Falls die Gegend in der Nähe einer der Positionen versperrt ist, wird die Wache den ganzen Tag versuchen, die Position zu erreichen;
- Die Wache hängt von Zeit zu Zeit fest

Wenn ich sehe dass Sie sich für den Code interessieren, werde ich ihn weiter verbessern.