Betreten eines Bereichs als Auslöser
Hallo GMOD-Community, ich habe eine Idee, weiss aber nicht wie ich diese in die Praxis umsetzen kann. Der Titel des Threads erklärt eigentlich schon was ich vorhabe. Ich möchte das eine bestimmte Aktion ausgeführt wird, wenn eine bestimmte Person einen bestimmten Bereich betritt. Ich möchte mir erstmal nur ein einfaches Beispiel machen.
Nehmen wir mal an ich möchte das sich eine Variable "counter" jedesmal um eins erhöht wenn der Spieler einen bestimmten Bereich betritt z.B. ein Feld das vor einem Eingang liegt. Kann mir jemand hierbei behilflich sein? Ich habe momentan leider kaum eine Idee wie ich das umsetzen kann :/
Vielleicht kann ich das Feld mit Vektoren erstellen?
Also so in etwa, den roten Text weiss ich nicht wie ich umsetzen soll:
Code:
local counter = 0
function GM:Think()
if ply:IsPlayer() and (ply:GetPos() == Innerhalb_des_Feldes) then
counter += 1
end
end
Danke für jede Hilfe!!
Gruß -Eddman
AW: Betreten eines Bereichs als Auslöser
Hab's nicht getestet, sollte aber klappen.
AW: Betreten eines Bereichs als Auslöser
Noch eine Kleinigkeit - du musst in einer Variable speichern wie der Status des Spielers (beim vorherigen durchgang der Funktion) war, also ob er innerhalb dieses Bereiches war oder nicht. Bei der if-Abfrage musst du dann überprüfen ob der aktuelle Status ungleich dem vorherigen ist. Da der Code bei jedem Frame ausgeführt wird würde der Wert nicht nur beim betreten sondern so lange er in dem bereich steht sehr schnell erhöht werden.
Des weiteren ist der Code Performance-Technisch nicht gerade optimal. Du solltest einen zweiten Bereich definieren der etwas größer ist. Diese Überprüfung wird aber nur jede Sekunde ausgeführt. Die Überprüfung mittels Think() wird erst dann ausgeführt wenn sich der Spieler innerhalb dieses Bereiches befindet. Somit müssen nicht ständig sämtliche Positionen aller Spieler überprüft werden, sondern nur die, die sch zumindest in der nähe des zu überprüfenden Bereiches befinden.
AW: Betreten eines Bereichs als Auslöser
Hi, Danke erstmal für eure Antworten. Thx für den Code, werde ich nach der Schule mal so testen.
Zitat:
Noch eine Kleinigkeit - du musst in einer Variable ...
Stimmt, danke für die Info!
Zitat:
Des weiteren ist der Code Performance-Technisch nicht gerade optimal.
I know, Ich hatte den Code sehr simpel gehalten, da es mir erstmal nur darum geht zu verstehen wie ich überhaupt vorgehen muss und ich ein schnelles Beispiel bräuchte. Danach kümmere ich mich um die Performance ;)
Zitat:
Du solltest einen zweiten Bereich definieren der etwas größer ist.
Könntest du mir bitte erklären wie du das meinst bzw. wie du es machen würdest?
Ich denke ich würde einen Timer verwenden der jede Sekunde ausgeführt wird, aber was soll der Auslöser vom Timer sein? GM:PlayerSpawn() vielleicht?
AW: Betreten eines Bereichs als Auslöser
Zitat:
Zitat von
Eddman
I know, Ich hatte den Code sehr simpel gehalten, da es mir erstmal nur darum geht zu verstehen wie ich überhaupt vorgehen muss und ich ein schnelles Beispiel bräuchte. Danach kümmere ich mich um die Performance ;)
Quellcode gleich ordentlich zu schreiben ist meist einfacher als diesen später umstrukturieren/umschreiben zu müssen. Vor allem weil man dann wieder Sachen übersieht/vergisst.
Zitat:
Zitat von
Eddman
Ich denke ich würde einen Timer verwenden der jede Sekunde ausgeführt wird, aber was soll der Auslöser vom Timer sein? GM:PlayerSpawn() vielleicht?
Ich erklär es dir am besten mit einem beispiel:
PHP-Code:
local checkPlayers = {} // Tabelle in der Spieler eingetragen werden bei denen die Position genau überprüft werden soll
// grobe positionsbestimmung
timer.Create('checkPlayerPos1', 1000, 0, function() // timer der alle 1000 ms (also jede sekunde) ausgeführt wird
for index, ply in pairs(player.GetAll()) do // alle spieler die am server sind durchgehen
local plyPos = ply:GetPos() // auslesen der position des spielers
if plyPos > Vector(100, 100, 100) and plyPos < Vector(200, 200, 200) then // befindet sich er spieler in der nähe des gewünschten punktes?
checkPlayers[ply:SteamID64()] = ply // spieler in die tabelle eintragen
elseif checkPlayers[ply:SteamID64()] ~= nil // spieler ist in der tabelle eingetragen, befindet sicher aber nicht mehr in der nähe des gewünschten punktes
table.remove(checkPlayers, index) // spieler aus tabelle löschen
end
end
end)
local counter = 0
local playersInPos = {}
// genaue positionsbestimmung
timer.Create('checkPlayerPos2', 100, 0, function() // genauere positionsbestimmung, timer der öfter läuft, 100 ms sollten für diesen zweck ausreichen, think ist eigentlich sowieso nicht nötig da die position sicher nicht so genau bestimmt werden muss
for index, ply in pairs(checkPlayers) do // tabelle checkPlayers durchlaufen
local plyPos = ply:GetPos() // auslesen der position des spielers
if plyPos > Vector(125, 125, 125) and plyPos < Vector(175, 175, 175) and playersInPos[ply:SteamID64()] ~= true then // befindet sich der spieler im vorgesehenen bereich?
counter = counter + 1 // variable um 1 inkrementieren
playersInPos[ply:SteamID64()] = true // zwischenspeichern das variable bei nächstem schleifendurchlauf nicht wieder inkrementiert wird
else
playersInPos[ply:SteamID64()] = false // spieler befindet sich nicht (mehr) im gewünschten bereich, zwischenspeicher löschen das variable erhöht wird wenn spieler den bereich wieder betritt
end
end
end)
Enthaltet vermutlich ein paar Fehler der Code da ich ihn nicht getestet habe.
AW: Betreten eines Bereichs als Auslöser
Zitat:
Zitat von
isch
Quellcode gleich ordentlich zu schreiben ist meist einfacher als diesen später umstrukturieren/umschreiben zu müssen. Vor allem weil man dann wieder Sachen übersieht/vergisst.
Das stimmt schon, aber ich will einfach nur verstehen wie das funktioniert.
Danke für dein Beispiel :up:, sieht von der Logik her einwandfrei aus und müsste funktionieren. Auch sehr gut und verständlich auskommentiert, vielen Dank! :up:
AW: Betreten eines Bereichs als Auslöser
@isch: Die Variante ist aber ziemlich schlecht, wenn man sich schnell(er) bewegt. Wenn man nun etwa schnell durch das Feld durch-noclipt, durchgespusht oder in einem Auto oder Seat durchfliegt, während man sich gerade nicht an dem Zeitpunkt befindet, in dem gechecked wird, wird der Trigger niemals ausgelöst. Gerade um die Ecken und Kanten des Feldes herum, wird das problematisch.
Die Variante die mMn am performantesten und geeignetsten ist, wäre ein Touch-Trigger (basierend auf collision-detection).
Dazu baust du dir zuerst ein Entity zusammen, bei welchem du per Variablen, Funktionen oder KeyValues Werte wie etwa den min- und max-Vector des Feldes setzen kannst.
Die Daten könntest du nun dazu verwenden, ein eigenes Kollisionsmodel für das Entity zu kreieren, per Ent.PhysicsFromMesh. Dazu musst du eigentlich nur die acht benötigten Punkte für ein Quader errechnen und in die Mesh-Table einfügen. Damit man nun nicht mit dem Ding kollidiert, die Trigger aber dennoch ausgelöst werden, musst du die Funktion Ent.SetCollisionGroup auf das Entity anwenden, und zwar am besten mit der Collision-Enumeration "COLLISION_GROUP_DEBRIS".
Um dann zu überprüfen wer oder was innerhalb des definierten Feldes steht/fliegt/whatevert, beziehungsweise was dann zu tun ist -etwa eine bestimmte Funktion für das jeweilige Entity ausführen, 'nen Counter inkrementieren oder sonst was-, musst du lediglich die Funktion(en) Ent.Touch und/oder Ent.StartTouch & Ent.EndTouch in dem Entity entsprechend ausprogrammieren.
Gerade mit Hilfe von Ent.Start/EndTouch lässt sich der Rechenaufwand auf ein Minimum reduzieren, während kein potenzieller "Triggerer" (=Auslöser) ignoriert wird.
Die Methode funktioniert zu 100%. Kann ich bezeugen, da ich diese in einem Gamemode selbst schonmal angewand habe.
AW: Betreten eines Bereichs als Auslöser
Hi RP-01, thx. Das werde ich mir auf jedenfall mal ansehen, aber leider hab ich noch keine Erfahrung mit Collision Detection, könntest du vielleicht mal ein einfaches Beispiel posten damit ich es verstehe? Wäre sehr nice, da ich es sehr gerne lernen würde! Vielleicht kannst du den Abschnitt aus deinem Gamemode einfach hier reinposten?
PS: Ich habe deine Variante versucht, bekomme aber folgenden Fehler ausgegeben:
[ERROR] gamemodes/test/gamemode/shared.lua:100: attempt to compare two userdata values
1. unknown - gamemodes/test/gamemode/shared.lua:100
Für <id des Players> habe ich 1 eingetragen, da dies ja der erste Spieler ist, der ja ich bin (da Einzelspieler).
Für Vector(low_x, low_y, low_z) habe ich Vector(963, -53, -79) und für Vector(high_x, high_y, high_z) habe ich Vector(1008, 170, 9) eingetragen, das ist bei der Map gm_construct eine Fläche vor dem Eingang links gleich neben den zwei schmalen Fensteröffnungen am ersten Gebäude wo man spawnt. Ich habe deinen Code bei <shared.lua> geschrieben
@isch deinen Code versuche ich jetzt auch gleich, mir ist aber ein Fehler bei deinem Timer aufgefallen, den delay muss man in Sekunden angeben nicht in Millisekunden ;) Also 1 für 1 Sekunde und nicht 1000.
EDIT: Konnte es mit meinem eigenen Code "lösen", aber leider kann ich den Timer ja nur auf 1 Sekunde minimalwert stellen, was viel zu viel ist... da der Auslöser zu spät erkannt wird. Wenn ich den Code mit GM:Think ausführe ist es kein Problem, ist aber eher die Notlösung denke ich. Ich lass mir was einfallen.
Ich muss aber noch herausfinden wie ich in der Bedinung abfragen kann ob eine bestimmte Map geladen ist, kann mir dabei jemand noch behilflich sein? Gibt es eine Abfrage wie "IsPlayer()" aber eben "IsMap()" oder so etwas ähnliches ?
Danke für eure Codes, teile davon werden mir noch sicherlich behilflich sein, z.B. den Teil des Codes von isch wo man anhand der SteamID prüfen kann welcher Spieler vom Auslöser betroffen werden soll.
PS: @RP-01 Interesse an deine Lösung mit "Collision Detection" besteht natürlich weiterhin ;)
Liste der Anhänge anzeigen (Anzahl: 1)
AW: Betreten eines Bereichs als Auslöser
Mein Code müsste wie folgt korrigiert werden:
Du kannst Vektoren in GMod zwar direkt vergleichen (==), nicht aber mit >, <, >= und <= vergleichen.
Zitat:
Zitat von
Eddman
EDIT: Konnte es mit meinem eigenen Code lösen, aber leider kann ich den Timer ja nur auf 1 Sekunde minimalwert stellen, was viel zu viel ist... wenn ich den Code mit GM:Think ausführe ist es kein Problem, ist aber eher die Notlösung denke ich. Ich lass mir was einfallen. Danke für eure Codes, teile davon werden mir noch sicherlich behilflich sein, z.B. den Teil des Codes von isch wo man anhand der SteamID prüfen kann welcher Spieler vom Auslöser betroffen werden soll.
Kommazahlen gehen selbstverständlich auch, wesshalb du als delay auch "0.001" eintragen könntest.
Und das in GM:Think() auszuführen, ist auch kein Problem. So z.B.:
Zitat:
Zitat von
Eddman
PS: @RP-01 Interesse an deine Lösung mit "Colision Detection" besteht natürlich weiterhin ;)
Das ist, wie gesagt, nicht mehr als das was ich beschrieben habe. Die StartTouch Hook-Funktion wird ausgelöst, sobald ein Objekt X theoretisch mit unserem beginnen würde zu kollidieren, während Touch so lange ausgelöst wird, wie ObjektX noch mit unserem Objekt kollidiert. EndTouch wird nur dann ausgelöst, wenn Objekt X aufgehört hat mit unserem Objekt zu kollidieren.
Für das was du erreichen willst, brauchst du eigentlich nur StartTouch.
Aber damit du siehst, wie das letztendlich richtig funktioniert, habe ich ein Beispiel mit normalem Helikopterbomben-Model angehangen.
Einfach per Spawnmenü unter "Others" spawnen, selbst durchgehen oder andere Objekte reinstecken, und auf das achten, was in die Konsole geschrieben wird.
AW: Betreten eines Bereichs als Auslöser
Hi, thx. Werd mal den überarbeiteten Code probieren, ich poste nach der Schule auch mal meine Lösung die nur das nötigste zum verstehen enthält, da ja das mit der Spielerüberprüfung erstmal zum verstehen des Auslöser an sich irrelevant ist, danach aber auf jeden fall wichtig!
Zitat:
Zitat von
RP-01
Du kannst Vektoren in GMod zwar direkt vergleichen (==), nicht aber mit >, <, >= und <= vergleichen.
Deswegen also..., schade das dies in keinem Wiki steht, oder hab ich was übersehn ? Über einen Umweg geht es doch, wie gesagt poste ich dann mal meine Lösung.
Zitat:
Zitat von
RP-01
Kommazahlen gehen selbstverständlich auch, wesshalb du als delay auch "0.001" eintragen könntest.
Das hatte ich natürlich ausprobiert, aber ich glaube ich habe eine Fehlermeldung bekommen, ich werde es heute nochmal nach der Schule testen, thx
So ähnlich sieht meine Lösung auch aus ^^ ich poste sie aber später trotzdem mal
Dank you für deine Hilfe :)