Seite 1 von 4 1234 LetzteLetzte
Ergebnis 1 bis 10 von 32

Thema: Lua Beginner's Guide - Part One

  1. #1
    Avatar von Scarecrow
    Registriert seit
    15.12.2005
    Ort
    Nordrhein-Westfalen

    Standard 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

    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

    Code:
    Player:IsAdmin()
    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:

    Code:
    Msg(i .. "\n")
    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 ;-)
    Geändert von Pac_187 (11.01.2008 um 15:58 Uhr) Grund: By Pac: Link gefixed!
    Welches ist die Höchstgeschwindigkeit einer unbeladenen Schwalbe?

  2. Folgende 19 Benutzer sagen Danke zu Scarecrow für den nützlichen Beitrag:


  3. #2
    €r!k
    Avatar von €r!k

    Standard 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

  4. Folgender Benutzer sagt Danke zu €r!k für den nützlichen Beitrag:


  5. #3
    Avatar von Scarecrow
    Registriert seit
    15.12.2005
    Ort
    Nordrhein-Westfalen

    Standard AW: Lua Beginner's Guide - Part One

    Zitat Zitat von €r!k Beitrag anzeigen
    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
    Welches ist die Höchstgeschwindigkeit einer unbeladenen Schwalbe?

  6. #4

    Standard AW: Lua Beginner's Guide - Part One

    Da hat sich einer aber viel Arbeit gemacht
    Saubere Arbeit Respekt

  7. #5

    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

  8. Folgende 2 Benutzer sagen Danke zu Pac_187 für den nützlichen Beitrag:


  9. #6
    Avatar von Scarecrow
    Registriert seit
    15.12.2005
    Ort
    Nordrhein-Westfalen

    Standard 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 ;-)
    Welches ist die Höchstgeschwindigkeit einer unbeladenen Schwalbe?

  10. #7

    Standard AW: Lua Beginner's Guide - Part One

    Pinned

  11. Folgender Benutzer sagt Danke zu Blackfinal für den nützlichen Beitrag:


  12. #8
    Avatar von Scarecrow
    Registriert seit
    15.12.2005
    Ort
    Nordrhein-Westfalen

    Standard 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
    Welches ist die Höchstgeschwindigkeit einer unbeladenen Schwalbe?

  13. #9

    Standard 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

  14. #10

    Standard 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
    Geändert von Bude132 (05.01.2008 um 21:48 Uhr)

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •