-
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 :)
-
AW: Betreten eines Bereichs als Auslöser
Guck dir auch den letzten Abschnitt bzw. das Entity an, was ich angehängt habe. Das ist nämlich genau das, was ich mit der Methode über Collision-Detection meinte. Über das Entity könntest du übrigens auch z.B. neh List von allen Objekten innerhalb des Entities (also auch Spielern) zum Gamemode-Code senden, um damit was auch immer zu machen.
-
AW: Betreten eines Bereichs als Auslöser
Werde ich machen, thx ;).
EDIT: Hier wie versprochen meine Lösung. Bei dieser Lösung wird der Code nur einmal ausgeführt.
<cl_init.lua>
PS: Weiss jemand wie ich überprüfen kann welche Map gerade gespielt wird?
EDIT: Ja es stimmt, man kann bei einem Timer auch 0.001 verwenden.
-
AW: Betreten eines Bereichs als Auslöser
Eine Suche nach "Map" in der Wiki wäre aufschlussreich gewesen. ;)
http://wiki.garrysmod.com/page/game/GetMap
-
AW: Betreten eines Bereichs als Auslöser
Ups, das habe ich wohl voll übersehen :-/ sry & thx :)
-
AW: Betreten eines Bereichs als Auslöser
Hi, ich hab nen kleines prob und möchte nicht extra nen neuen Thread aufmachen.
Weiss jemand wie ich eine Abfrage hinbekomme die überprüft ob das Objekt das den Auslöser betritt ein Entity ist? Ich suche nach einem Befehl der so ähnlich wie IsPlayer() ist, also z.B. IsEnt(). Ich finde absolut keine.
-
AW: Betreten eines Bereichs als Auslöser
Das sollte eigentlich das sein, was du suchst. Allerdings denke ich mir, dass du etwas suchst, was dir ausgibt ob etwas ein Player, ein NPC oder ein anderweitiges Entity ist, richtig? Denn Player und NPCs sind in GMod ebenso Entities, wie es der Bouncy Ball oder der worldspawn es sind.
Dazu gäb's dann diese Lösung:
Wenn man nämlich type auf einen Player oder eine Weapon anwendet, gibt er das auch als differenzierten Typ aus. Somit würdest du bei IsEnt( player.GetByID(1) ) z.B. false rausbekommen.
Kannst natürlich auch das ganze drumherum weglassen, und nur den type( var ) == "Entity" -Part verwenden.
-
AW: Betreten eines Bereichs als Auslöser
Danke, war mein Fehler, ich habe nur auf dieser Website gesucht , da ich dachte sie sei vollständig.
Zitat:
Allerdings denke ich mir, dass du etwas suchst, was dir ausgibt ob etwas ein Player, ein NPC oder ein anderweitiges Entity ist, richtig?
Richtig :) nun, was ich konkret vorhabe ist, das irgendwas passiert, meinetwegen eine Konsolenausgabe, wenn z.B. ein SentBall in den Auslöser gelangt. Ich werde es mal mit deinem Beispiel testen, danke :)
-
AW: Betreten eines Bereichs als Auslöser
Hi, wollte es gerade testen, aber mir fällt nicht ein was ich dann z.B. für sentball bei der variable "var" eintragen muss, kann mir wer helfen?
Danke im Vorraus :)
-
AW: Betreten eines Bereichs als Auslöser
Neh Variable. Beispiel:
Letztes Argument müsste stets als "true" ausgegeben werden.
-
AW: Betreten eines Bereichs als Auslöser
hmm, ich bekomm es einfach nicht hin. Ich möchte das wenn ein sentball den Auslöserbereich betritt, das es dann in die Luft katapultiert wird.
Aber auch mit deinen Funktionen will es nicht hinhauen :/
Mit dem Spieler klappt das schon.
Wie löse ich das "IsSentball", ich habe schon etliche versuche unternommen ohne erfolg :( :?:
-
AW: Betreten eines Bereichs als Auslöser
Probiers mal mit v:GetClass() == ''sent_ball" :)
-
AW: Betreten eines Bereichs als Auslöser
Zitat:
Zitat von
minifisch
Probiers mal mit v:GetClass() == ''sent_ball" :)
Klappt nicht, soweit ich weiss gibt GetClass() nur etwas wie "prop_physics" aus. Habe es auch getestet, da passiert nichts mit meinem SentBall. Trotzdem thx. Sònst noch jemand Ideen?
-
AW: Betreten eines Bereichs als Auslöser
Natürlich klappt das, du wendest bloß SetVelocity falsch an (Siehe hier). GetClass() gibt dir den Namen zurück, auf egal welches Entity du das anwendest. prop_physics ist auch nur ein Entity, bloß eben "hardcoded". Hat damit also gar nichts zu tun.. (Die Funktion gibt dir also auch Namen von Entities zurück, welche selbst gecoded sind.)
Das sind beides Varianten, die funktionieren würden:
-
AW: Betreten eines Bereichs als Auslöser
Wow, danke es funktioniert endlich :) :up: Und wieder etwas dazugelernt, danke euch !!! :up:
Edit: Mit dem Sentball klappt es schonmal, aber mit anderen Objekten z.B. der Waschmaschine scheint es mit diesem Code nicht zu funktionieren (hab den Code natürlich angepasst), wenn ihr Tipps habt, dann nur raus damit ;)
-
AW: Betreten eines Bereichs als Auslöser
Habe es mal so probiert, aber es klappt nicht. Vermutlich unterscheidet sich die Waschmaschine vom sent mehr als ich gedacht habe. Ist es überhaupt möglich normale Objekte wie eine Waschmaschine für solche Dinge zu verwenden?
GetName() funzt leider auch nischt..
-
AW: Betreten eines Bereichs als Auslöser
Wieso hat es mit dem sent_ball geklappt, aber mit anderen dingen nicht? :/
-
AW: Betreten eines Bereichs als Auslöser
"models/props_c17/FurnitureWashingmachine001a.mdl" ist doch keine Klasse. Das ist ein Teil von 'nem Model/Mesh.
Versuch's damit:
Edit:
BTW: Hier elseif zu nutzen, ist unnötige Rechenarbeit. Der führt nämlich dann jedes mal noch 'nen zweiten Check durch, obwohl v:GetClass() == "sent_ball" war.
Benutze hier, wie ich, einfach eine zweite if-clause, und springe per "continue" in den nächsten Schleifendurchgang, wenn das Entity schon ein sent_ball war
-
AW: Betreten eines Bereichs als Auslöser
Ich hab zwar schon lange nixmehr mit LUA in Gmod gemacht und weiß deshalb nicht, ob das noch aktuell ist, aber...: Muss man bei einem PhysicObject nicht ApplyForceCenter verwenden?
-
AW: Betreten eines Bereichs als Auslöser
Nicht unbedingt. Kann man, alternativ zu Ent:SetVelocity() oder Ent:GetPhysicsObject():SetVelocity() nutzen..
SetLocalVelocity gibt's auch noch, aber ich glaube das hat die Velocity dauerhaft gesetzt.
Die anderen Befehle sollten jeweils eine Velocity zu dem entsprechenden Vektor addiert haben.
Muss ich später mal testen, was da genau der Unterschied war. Mein Code von oben funktioniert jedenfalls.
-
AW: Betreten eines Bereichs als Auslöser
Zitat:
Zitat von
RP-01
BTW: Hier elseif zu nutzen, ist unnötige Rechenarbeit. Der führt nämlich dann jedes mal noch 'nen zweiten Check durch, obwohl v:GetClass() == "sent_ball" war.
Damit hast du vollkommen recht, gut zu wissen. Werde ich in Zukunft nun auch immer so machen. :up:
PS: Danke für deine Verbesserungen und Hilfe, ich lerne immer mehr :)
Edit: Hmm, hab alles so umgeschrieben, es scheint immernoch nicht zu funktionieren, ich poste später mal meinen Code.
-
AW: Betreten eines Bereichs als Auslöser
Ok, habe gerade festgestellt das das Problem an der Waschmaschine lag, mit dem Roten Fass funktioniert es tadellos.
Weiss jemand weshalb?
-
AW: Betreten eines Bereichs als Auslöser
Auf lower-/uppercase geachtet?
Habe ich nämlich gemacht, und bei mir ging's. Ent:GetModel() gibt 'nen lowercase String zurück, und wenn du das mit 'nem String vergleichst der uppercase Letters hat, dann gibt der bei dem Vergleich natürlich false zurück.
-
AW: Betreten eines Bereichs als Auslöser
Ha, es lag tatsächlich daran, wusste nicht das es case sensitive ist, ich hatte es direkt vom Spiel auf das ClipBoard kopieren lassen und dachte mir das wird schon so passen, denkste... Danke dir :)