-
Lua Beginner's Guide - Part One
Lua Beginner's Guide - Part One - by ComodoFox alias Scarecrow
Vorwort:
Ich habe mir letztens (Jahr 2007) Garry's Mod 10 gekauft und wollte natürlich direkt
mit Lua beginnen, also suchte ich nach Tutorials... und suchte... und suchte...
und fand nichts, außer einfachen Basics von Lua. Über den Einstieg in Garry's Mod
Programmierung fand ich leider so gut wie nichts. Also begab ich mich auf den
eisernen Weg des "Learning By Doing" und will euch meine Erkenntnisse nicht
vorenthalten. Das heisst, dass ich selbst noch ein Neuling bin (auch wenn ich mich
mit C++ beschäftige). Verbesserungsvorschläge sind also immer drin ;-)
PS: Mitllwerweile kann ich es exellent, das Ganze Geschwafle oben ist ein Jahr alt :D
Viel Spaß beim Scripten!
Basics:
Bevor ihr euch dieses Tutorial durchlest, solltet ihr schon einmal die Basics von
Lua drauf haben. Die könnt ihr euch z.B. hier durchlesen:
http://lua.gts-stolberg.de/
Ansonsten auch mal im Facepunch vorbeischauen :-)
Something new:
Generell ist die Scriptsprache Lua eine einfache Sprache. Jedoch machen die
Implementationen mit Games und Programmen aus Lua einen fetten Schinken,
bei dem der Eine schnell den Überblick verlieren kann. So gibt es insbesondere hier
einiges zu erwähnen:
Funktionen - Jeder kennt sie (siehe Basics), die Funktionen, und wenn man schon
daran denkt, dass
Code:
_PlayerSetHealt(player, health)
den Health-Wert auf einen Anderen setzt, dann "fühlt man sich zuhause". Aber
neuerdings heisst der Befehl
Code:
Player:SetHealth( health )
Jetzt stellt sich die Frage: Was ist das mit dem Doppelpunkt?
Ganz einfach: Alles vor dem Doppelpunkt, also Player, nennt man (glaube ich)
Objekt. Ein Objekt ist damit also wie eine Variable. Ich habe mir sagen
lassen, dass Garry dieses wegen der leichten Schreibweise eingebaut hat.
So kann man z.B. mit einem Befehl, der einen Spieler ausgibt folgendes bauen:
Code:
spieler = FunktionGibtSpielerAus()
spieler:SetHealth( 200 )
Die Variable spieler wird zu dem, was FunktionGibtSpielerAus() ausgibt, z.B. "ComodoFox".
Dann wird mit spieler:SetHealth( 200 ) dem Player "ComodoFox" ein Healthwert von
200 gegeben, weil wir Player durch einen Namen eines Spielers ersetzt haben, nämlich
durch spieler und weil diese Variable den Inhalt trägt, den FunktionGibtSpielerAus() ausgegeben hat,
sieht das im Endeffekt so aus:
Code:
ComodoFox:SetHealth( 200 )
Aber das macht man nicht, deshalb definiert man ein Objekt, weil der Inhalt dynamisch, veränderbar, wird ;-)
Hooks - Wie der name schon vermuten lässt, sind Hooks einfach nur Haken, die an etwas
ziehen. Jetzt mal genauer: Ein Hook ist eine bestimmte Situation im Spiel, die dann
eine Funktion aktiviert. Z.B. ist mit dem Hook GM:PlayerConnect die Situation eines
Connects verbunden. Wenn jemand connected, dann wird jede Funktion ausgeführt, die
mit GM:PlayerConnect in Verbindung gebracht wurde.
Diese Verbindung entsteht durch den Befehl
Code:
hook.Add(name, unique_name, function)
wenn wir uns die Funktion GibDemAffenZucker() gebaut haben und sie mit dem Connecten eines
Players verbinden wollen, dann lautet der Befehl:
Code:
hook.Add("PlayerConnect", "GibDemAffenZuckerHook", GibDemAffenZucker)
name ist der Name des Hooks, den wir in Verbindung setzen wollen, also mit GM:PlayerConnect(gm weglassen).
unique_name ist der einzigartige Name unseres neuerstellten Hooks. Ich habe ihn liebevoll
GibDemAffenZuckerHook genannt.
Und function ist die Funktion, die wir auslösen wollen, wenn der Hook zutrifft.
Wenn ein Spieler connected, wird automatisch die Funktion GibDemAffenZucker() ausgeführt.
TIPP: Funktionen und Hooks findet ihr im englischsprachigen Gmod.Wiki, leider ist mir der Link
entfallen, obwohl ich da jeden Tag drauf bin, mein guter, alter Freund Goo Gell (www.google.de) kann euch
aber helfen =D
Server vs. Client - Früher war alles einfach: Luascript fertig gemacht, auf Server gepackt, funktioniert.
Aber es gab einen entscheidenden Nachteil: Alles, was im Script ist, und damit auch die Berechnung von
Sachen wie Farben, Modellen, etc, musste auf dem Server berechnet werden.
In Gmod10 gibt es Serverside Scripts und Clientside Scripts. Es gibt kein Muss, aber wenn ihr
Sachen, die berechnet werden müssen (Farben, Modelle, Effekte etc.) dem Clienten überlasst,
dann muss der Server nicht so viel berechnen (Nur Kollisionen, Standorte etc.) und hat damit
einen besseren Ping. In Zukunft werdet ihr also ein wenig darauf achten, aber alles zu seiner
Zeit ;-)
Jetzt wird es Zeit für ein echtes, kleines Script...
Scripting Example No. 1:
Jetzt schreiben wir unser eigenes Script, welches euch kaum was bringt und nicht mal mit
SteamIDs arbeitet, aber die Basis für beispielweise RP-Scripts ist.
Stellt euch vor, ihr habt einen eigenen Server, und ihr möchtet erfahren, welche Spieler
auf eurem Server bereits wahren. Die Theorie ist also folgende:
Wenn ein Spieler euren Server betritt, wird geprüft, ob er schonmal hier war, seit dem
der Server gestartet wurde. War er schon mal da, passiert nichts, war er noch nicht da,
dann wird er in eine Liste eingetragen, die vorübergehend existiert. Mit einem
Konsolenkommando, welches nur der Admin benutzen kann, wird die Liste in der Konsole ausgegeben.
Jetzt die Umsetzung:
Zunächst einmal benötigen wir die Funktion, die überprüft, ob der Spieler, der connected ist,
schon einmal da war. Wenn nicht, soll sie ihn eintragen:
Code:
playertab = {}
function PlayerConnects( ply, adr, steam )
local vorhanden = 0
//Wenn es den Player in der Liste gibt, dann wird er nachher nicht eingetragen
for i,v in pairs(playertab) do
if i == ply then
vorhanden = 1
return
end
end
//Gibt es ihn nicht, wird er in die Liste eingetragen
if vorhanden == 0 then
playertab[ply] = 1
end
end
playertab = {} definiert einen Table (siehe Basics), den wir später brauchen.
Dann wird eine Funktion namens PlayerConnects definiert, die 3 Argumente hat. ply ist der Spieler,
adr die Adresse (brauchen wir überhaupt nicht) und steam die SteamID (ich hatte bei SteamIDs
letztens einen Fehler). Wichtig ist hier ply, denn dort wird später der Name des Spielers gespeichert.
Wahrscheinlich fragt ihr euch, warum ich die Argumente in meine Funktion einbaue, obwohl ich sie
garnicht brauche? Einfach deshalb, weil der Hook diese Argumente beim Aktivieren ausgibt, und die
müssen sozusagen auch in der Funktion untergebracht sein.
local vorhanden = 0 definiert eine lokale Variable, die später für etwas nützlich wird.
Und jetzt kommt eine for-Schleife (siehe Basics). Mit ihr wird in i der Tablename(Nummer der Tables), z.B.
"ComodoFox" gespeichert und in v der Inhalt, der eigentlich egal ist.
Dann wird geprüft, ob i dem Namen des gerade angekommenen Spielers, ply, entspricht.
Dass wird jetzt durch die For-Schleife mit jedem Namen in playertab getan.
Wenn es einen Namen gibt, der dem gerade connectendem Spieler entspricht, wird der Variable
vorhanden der Wert 1 zugewiesen und durch return wird die Schleife beendet.
Jetzt geht die Funktion weiter, indem geprüft wird, ob vorhanden gleich eins ist.
Wir erinnern uns daran, dass hier 1 für vorhanden und 0 für nicht vorhanden steht.
Der Befehl playertab[ply] = 1, der einem Table den Namen unseres Players gibt und den Wert
eins zuweist (der Wert ist unwichtig, der Name zählt), wird nur ausgeführt, wenn vorhanden
auf null gesetzt ist. Es bedeutet, dass vorhin in der Liste kein Spieler mit diesem Namen
gefunden wurde.
Jetzt setzen wir die Funktion mit einem Hook in verbindung (schreibt das unter die Funktion):
Code:
hook.Add("PlayerConnect", "PlayerConnectsServer", PlayerConnects)
"PlayerConnect" ist der Hook, vergesst die Anführungszeichen nicht ;-)
"PlayerConnectsServer" ist mein selbst definierter Name für den eigenen Hook.
PlayerConnects ist einfach nur der Name unserer Funktion, aber ohne ().
Das speichert ihr nun in:
Code:
steamapps\[STEAMACCOUNTNAME]\garrysmod\garrysmod\lua\autorun\server\spieler_check.lua
Alle Scripts in diesem Ordner werden automatisch auf dem Server ausgeführt, wenn er gestartet wird.
Wir sind aber noch lange nicht fertig, denn uns fehlt ja noch das versprochene Konsolenkommando:
Code:
function GiveOutPlayerTab( ply, command, args )
if !ply:IsAdmin() then return end
Msg("\n\n---Players in list---\n")
Msg("------------------------------\n\n")
for i,v in pairs(playertab) do
Msg(i .. "\n")
end
Msg("\n------------------------------\n")
end
Ich definiere hier die Funktion GiveOutPlayerTab mit drei Argumenten. ply ist der Spieler, der das
Konsolenkommando auslöst. command habe ich vergessen (im Gmod.Wiki unter concommand nachschauen,
wenn es wieder online ist) und args sind die Argumente, die beim Konsolenkommando übergeben werden.
Z.B. sv_cheats 1 - eins wäre ein Argument (tolles Beispiel =D).
Diese drei Argumente sind vorausbestimmt, denn wenn wir die Funktion mit einem Konsolenkommando in
Verbindung bringen, dann werden diese Argumente der Funktion übergeben, ähnlich wie bei den Hooks.
Dann wird durch die IF-Anweisung geprüft, ob der Spieler ply, der dieses Kommando aufruft, überhaupt
ein Admin ist. Der Befehl
gibt true aus, wenn Player, bei uns ply, ein Admin ist und false, wenn nicht.
Code:
if ply:IsAdmin() then return end
würde heissen: "Wenn ply ein Admin ist, dann Beenden." Aber ich habe dort stehen:
Code:
if !ply:IsAdmin() then return end
Das Ausrufezeichen macht den Befehl negativ, also geht von ungleich eins aus, denn ihr
müsst wissen, dass true für 1 und false für 0 steht. Mit dem Ausrufezeichen geht man
also auf das Gegenteil von true ein, hiese also: "Wenn ply kein Admin ist, dann Beenden."
Man könnte also auch
Code:
if ply:IsAdmin() == 0 then return end
schreiben oder 0 gegen false ersetzen, aber mit dem Ausrufezeichen geht alles einfacher.
Die nächsten zwei Befehle sind Msg("string"). Diese geben etwas in der Konsole aus, nützlich
für Debugging. Und vergesst das Newline-Zeichen \n nicht, oder wollt ihr alles in einer
einzigen Zeile haben? ;-)
Wichtig ist dort wieder die for-Schleife. Sie geht wie immer durch alle Inhalte des genannten
Tables playertab und führt ihre Befehle im Block aus:
i steht für den derzeitigen Tablenamen, z.B. "ComodoFox" (playertab["ComodoFox"]) und gibt ihn aus.
Danach folgt ein Newline-Zeichen für den nächsten Namen. Die zwei Punkte sind zur Abgrenzung von
Variablen und Strings da bzw. für alle Abgrenzungen. Beispiel:
Code:
Msg("Ich bin " .. i .. " Jahre alt, und du bist nur " .. j .. " Jahre alt.\n")
Das würde dann folgendes ausgeben (wenn i 50 und j 30 sind):
Code:
Ich bin 50 Jahre alt, und du bist nur 30 Jahre alt.
Fehlt nur noch die Vereinbarung zu einem Konsolenkommando:
Code:
concommand.Add( "check_player_tab", GiveOutPlayerTab )
Das erste Argument ist der String, wie unsere Funktion nachher aufgerufen wird.
Das Zweite ist dann die Funktion, ohne "". Ein drittes Argument gibt es auch, die
Autovervollständigung. Wenn man das Kommando ohne Argumente eingibt, dann werden
die einzugebenden Argumente dargestellt. Aber das ist nur optional und total unnütz,
wenn euer Kommando keine Argumente übergeben soll.
Das ist dann das fertige Script:
Code:
// Lernscript von ComodoFox alias Scarecrow (im Gmod.de-forum)
// Gebraucht: 2 Funktionen (Connect und Disconnect) mit 2 Hooks verbunden
//Wichtige Variablen
playertab = {}
//Für den Hook bei Connecten eines Players
function PlayerConnects( ply, adr, steam )
local vorhanden = 0
//Wenn es den Player in der Liste gibt, dann wird er nachher nicht eingetragen
for i,v in pairs(playertab) do
if i == ply then
vorhanden = 1
return
end
end
//Gibt es ihn nicht, wird er in die Liste eingetragen
if vorhanden == 0 then
playertab[ply] = 1
end
end
//Der entgültige Hook
hook.Add("PlayerConnect", "PlayerConnectsServer", PlayerConnects)
function GiveOutPlayerTab( ply, command, args )
if !ply:IsAdmin() then return end
Msg("\n\n---Players in list---\n")
Msg("------------------------------\n\n")
for i,v in pairs(playertab) do
Msg(i .. "\n")
end
Msg("\n------------------------------\n")
end
concommand.Add( "check_player_tab", GiveOutPlayerTab )
Schön abspeichern und testen: Macht ein Singleplayer-Spiel (oder Multplayer) auf und gebt in die
Konsole check_player_tab ein. Euch wird, wenn alles funktioniert, eine Liste mit allen Spielern
angezeigt, die seit dem ihr euren neuen Server gestartet habt, connected sind. Bei mir kam das heraus:
Code:
---Players in list---
------------------------------
ComodoFox
------------------------------
TIPPS:
- Ihr könnt auf diese Art und Weise auch Roleplay Scripts bauen, dann aber mit SteamID
in eine Datei speichern.
- Statt dem Namen könnt ihr auch die SteamID nutzen, aber auf LAN-Servern bringt euch das
wenig.
- Hooks, die mit PlayerConnect verbunden werden, sind gefährlich. Denn wenn ihr einen Listenserver
erstellt, connected ihr, wenn noch nicht einmal alle Funktionen geladen wurden. Deshalb erhaltet
ihr vielleicht NIL VALUEs in der Konsole. In diesem beispiel habe ich deshalb Konsolenkommandos
genutzt, weil sie erst nach dem Connecten benutzbar sind.
- Vergesst nicht, zu kommentieren. Wenn ihr viel kommentieren müsst, lasst es sein. Schreibt lieber
das Script einfacher ;-)
- Benutzt das englische Gmod.Wiki. Dort steht alles, was ihr braucht.
Wenn ihr Fragen habt, dann fragt!
Bis zum nächsten Teil ;-)
-
AW: Lua Beginner's Guide - Part One
Sehr gut! Würde ich direkt anpinnen. Währe gut wenn du es noch in die GMod.de Wiki übertragen würdest ;)
-
AW: Lua Beginner's Guide - Part One
Zitat:
Zitat von
€r!k
Sehr gut! Würde ich direkt anpinnen. Währe gut wenn du es noch in die GMod.de Wiki übertragen würdest ;)
Sowas gibt es? Ja dann muss ich da mal vorbeischauen! Danke für den Hinweis ;)
-
AW: Lua Beginner's Guide - Part One
Da hat sich einer aber viel Arbeit gemacht :)
Saubere Arbeit :up: Respekt ;)
-
AW: Lua Beginner's Guide - Part One
Was du vielleicht noch adden kannst ist, dass "Player"
in den SWEPs immer für den Spieler steht und deshalb dort:
self.Owner
stehen muss:
Code:
self.Owner:SetHealth( 100 )
Es sei denn man hat sich vorher einen "shortcut" [so nenn ich es immer] gebastelt:
Code:
local Player = self.Owner
Player:SetHealth( 100 )
----------
-> Gute Arbeit, weiter so ;)
Pac_187
-
AW: Lua Beginner's Guide - Part One
@Pac_187: Nun ja, SWEPs hab ich nicht so oft gemacht, aber SENTs und daher weiß ich, was du meinst. In irgendeinem nächsten Teil erwähne ich das ;-)
-
AW: Lua Beginner's Guide - Part One
-
AW: Lua Beginner's Guide - Part One
@Pac: Das hat alles mit OOP in Lua zu tun, denn in einer Datei bzw. einem Script kann sich das eigene Objekt durch "self" aufrufen. Das gibt es auch in anderen Sprachen (z.B. C++,AS1 und 2 etc), weshalb ich dachte, dass das Ganze klar sei ;)
-
AW: Lua Beginner's Guide - Part One
Das du es in V10 Lua via self und nicht z.B. durch self.Entity aufrufen kannst ( was auch geht ),
gibt es aber erst seit dem letzten Update ;)
-
AW: Lua Beginner's Guide - Part One
Hi, ich will lua Coding lehrnen und dein Topic hat mir Sehr geholfen. Freu mich schon auf die Fortsetzung ;)
Hir ist der link für das Programm (NotePad), Wenn man es noch nich hatt ;) http://sourceforge.net/project/showf...kage_id=102072
Es währe cool wen Du auch mal so ein Tutorial für Ein Swep machen würdest :)
-
AW: Lua Beginner's Guide - Part One
-
AW: Lua Beginner's Guide - Part One
@Pac_187 Dieser link
Zitat:
Zitat von [url
http://sourceforge.net/project/showfiles.php?group_id=95717&package_id=102072[/url]
ist doch von der seite
-
AW: Lua Beginner's Guide - Part One
Ja du hast deinen Beitrag etwas später editiert...
Wo liegt jetzt dein Problem?
Zie die zu öffnende Lua Datei doch einfach in das Programm rein und fertig, dann
kannst sie editieren.
-
AW: Lua Beginner's Guide - Part One
Nein das hatt sich erledicht ich Hatte das Programm nicht en freund hatt mir gesagt wass dafür benötigt wird Und ich will kein Swep öffnen ich will eins erstellen
-
AW: Lua Beginner's Guide - Part One
Guck mal in der Gmod.de Wiki nach.
Da habe ich ein Framework reingestellt, welches dir Zeig wie eine SWEP
aufgebaut ist und wo für welche Funktion ist.
-
AW: Lua Beginner's Guide - Part One
Hey dank Dir :)
€idt: hey, aber hab en Problem wie kann ich das SWEP benutzen?
Das ist nich in der WaffenListe
-
AW: Lua Beginner's Guide - Part One
erstelle einen Ordner namens weapon_NAME
( anstatt von NAME setzt du einen Namen ein Ohne(!) Leerzeichen! )
in:
x:\Valve\Steam\SteamApps\ACC_NAME\garrysmod\garrys mod\lua\weapons\
In deinen Ordner packst dann die Lua Datei rein, welche folgenden Namen
haben sollte:
shared.lua
Nun sollte die Waffe bei dir im Menü auftauchen, wenn nicht, dann hast du
entweder etwas falsch gemacht oder du hast das Script so geändert das es
nicht mehr funktioniert.
-
AW: Lua Beginner's Guide - Part One
Ach Damn hab was falsch gemacht... aber Trotzdem danke
-
AW: Lua Beginner's Guide - Part One
Ich hab mir eben die Lua Basics reingezogen und ich muss sagen, dass es wirklich kompliziert ist aber verstanden hab ichs :D
-
AW: Lua Beginner's Guide - Part One
ich wies es gehört nicht hir rein aber hir ist mein erstes swep :D
http://www.garrysmod.org/downloads/?a=view&id=28826
-
AW: Lua Beginner's Guide - Part One
Zitat:
Zitat von
Bude132
File not found? :|
-
AW: Lua Beginner's Guide - Part One
lol lua kommt mir ein bisschen wie wire for nur mit mehr möglichkeiten xD
was mache ich falsch? (hab gestern angefangen)
Code:
player1health = 100
player1death = 0
if player1health > player1death then
print ("Player is ALIVE!")
end
if player1health <= playerdeath then
print ("Player is DEAD!")
end
DESWEGEN:
Code:
player1health = 100
death= 0
if player1health > death then
print ("Player1 is ALIVE!")
else
print ("Player1 is DEAD!")
end
-
AW: Lua Beginner's Guide - Part One
Was auch immer du da versucht hast, in Garry's Mod lässt sich das so nicht realisieren.
Desweiteren wurden alle Bauteile von Wire mit Lua geschrieben!
-
AW: Lua Beginner's Guide - Part One
ich lern ja noch ;)
Naja azuerst ist es mir wie wire logik vorgekomen aber...
WEgen den basics: was heißt verdammt nochmal add blocked by SPF?
(geht mir schon lange (-----))
-
AW: Lua Beginner's Guide - Part One
Der link zu den Basics geht nicht mehr?
-
AW: Lua Beginner's Guide - Part One
Die Weiterleitung ist nur Fehlerhaft:
http://lua.gts-stolberg.de/
-
AW: Lua Beginner's Guide - Part One
-
AW: Lua Beginner's Guide - Part One
Zitat:
Zitat von
WeltEnSTurm
ich lern ja noch ;)
Naja azuerst ist es mir wie wire logik vorgekomen aber...
WEgen den basics: was heißt verdammt nochmal add blocked by SPF?
(geht mir schon lange (-----))
Du hast ja im Prinzip recht, denn Wire, Lua, C++, das sind alles logische Mittel zum Zweck. Das dir das gleich vorkommt, ist also begründet. Es ist immer nur die Umsetzung, die anders ist.
-
AW: Lua Beginner's Guide - Part One
Zitat:
Zitat von
WeltEnSTurm
lol lua kommt mir ein bisschen wie wire for nur mit mehr möglichkeiten xD
was mache ich falsch? (hab gestern angefangen)
Code:
player1health = 100
player1death = 0
if player1health > player1death then
print ("Player is ALIVE!")
end
if player1health <= playerdeath then
print ("Player is DEAD!")
end
DESWEGEN:
Code:
player1health = 100
death= 0
if player1health > death then
print ("Player1 is ALIVE!")
else
print ("Player1 is DEAD!")
end
versuchs mal mit
Code:
Playerhealth = 100
if Playerhealth ~= nil then
print ("PLAYER IS ALIVE")
else
print ("PLAYER IS DEAD")
end
-
AW: Lua Beginner's Guide - Part One
@ nowai
Er hat das Problem bereits gelöst.
Dieses "DESWEGEN" wurde hinein editiert ;)
-
AW: Lua Beginner's Guide - Part One
Zitat:
Zitat von
Pac_187
@ nowai
Er hat das Problem bereits gelöst.
Dieses "DESWEGEN" wurde hinein editiert ;)
Okay, dann hab ichs falsch verstanden ;)
-
AW: Lua Beginner's Guide - Part One
Dieses TUT ist wirklich gut gemacht ich freuhe mich drüber das jemand sich mal zeit dafür gelassen hat ++ von mir