Lua:Tutorial GettingStarted: Difference between revisions

From Spring
Jump to navigationJump to search
Knorke (talk | contribs)
→My first wupdget: folder Widgets\ might not exist
8611 (talk | contribs)
edited with better beginner widget
Line 1: Line 1:
[[Engine_Development|Development]] < [[Lua_Scripting|Lua Scripting]] < {{FULLPAGENAME}}
[[Engine_Development|Development]] < [[Lua_Scripting|Lua Scripting]] < {{FULLPAGENAME}}


==What this is==
<code><pre>
This page explains how to get started making scripts for spring.
 
==What this is not==
This page does not explains how to make scripts for spring.
 
This page is not a Lua tutorial.
 
==Requirements==
* having played spring game for some days
* text editor with synthax coloring
* copy of [[SpringTutorialGame]]
* [http://springfiles.com/spring/spring-maps/aliendesert some small map] because those load faster
* Spring set to [[Springsettings.cfg#Fullscreen|windowed mode]] for easier switching between editor/game
* a very bit familiar with programing (variables, functions, loops, arrays = enough)
* read through [[TestingYourGame|this tips page]] once
 
==Wupget and friends==
In Spring games there are 3 kinds of Lua scripts.
Crude and simple explaination:
# gadgets: these are the "gameplay logic code."
# widgets: these are for interface.
# unit scripts: animate units & controll their weapons.
 
Making those 3 is not very different but widgets are the easiest to start with.
 
There is also 4) functionality provided by the engine.
 
'''Also look at this:'''
 
http://springrts.com/mediawiki/images/1/13/Gadgets_widgets_and_friends.png
 
All 4 are in a constant fight against each other.
 
==My first wupdget==
The [[SpringTutorialGame]] is simple and good for testing:
You can use any other game but some games block player widgets or are very confusing.
 
Create a txt file like this and there:
''Spring\LuaUI\Widgets\myfirstwidget.lua''
 
If the ''Widgets'' folder does not exist, then create it.
 
 
<span class="warning">Alternative way:</span>
If you already know how to edit game-archives then you can also put''myfirstwidget.lua'' inside the game-archive.


For example: ''SpringTutorialGame.sdd\LuaUI\Widgets\myfirstwidget.lua''
Copypaste this text into the myfirstwidget.lua :
-----
<code><pre>
function widget:GetInfo()
function widget:GetInfo()
  return {
  return {
    name     = "my first widget",
      name         = "Name as shown in widget list",
    desc     = "this is so awesome",
      desc         = "",
    author   = "horse 3000",
      author         = "john doe",
    date     = "2009",
      date         = "date",
    license   = "GNU GPL, v2 or later",
      license         = "PD",
    layer     = -10,
      layer         = 0,
    enabled   = true
      experimental  = false,
  }
      enabled         = true
  }
end
end
</pre></code>
-----
For now it does not matter what excactly this does but here you can put informations about your widget.
What matters is that without such ''GetInfo()'' function Spring will not load the widget correctly.
It is usually not interessting and will be left out when code is posted, to save screen space.
Of course it must still be there.
Start spring.exe, load a game and press F11. The "widget menu" opens:
http://springrts.com/mediawiki/images/8/80/Widget_F11menu.png
It is likely already filled with some other widgets contained in the game.
Find your new widget in the list. Click on it to turn it on and off: The name in list should go green or red accordingly.
If that works - congratulation: You made a new widget that does excactly nothing.
="Hello unit" wupdget=
Add the following at the end of myfirstwidget.lua:
<code><pre>
function widget:UnitFinished(unitID, unitDefID, teamID)
Spring.Echo ("a new unit was finished! It belongs to team " .. teamID)
end
</pre></code>
UnitFinished(...) is a [[Lua:Callins|callin function]] : Those are functions that the engine calls when certain events happen.
Captain Obvious has already figured out: This function gets called when a new unit is finished.
Spring.Echo is used to output text to the chat/console.
So this widget should tell when a new unit is made.
4)
Open the widget menu and disable then re-enable your widget: This will reload the widget.
Now build some units to test it.
===Finding errors and infolog.txt===
For test add an error like this:
Spring.Echo (100 + nil)
and reload the widget.
The widgets name is green but turns yellow as soon a unit is build: "nil" can not be used in math like that.
The error is also displayed in chat and [[infolog.txt]]
In that logfile you can look up all errors even if they have disappeared from chat.
Fix the error, reload the widget and it is green again. Oha.
FIXME: expand this
===widget sees you and only you and your allies===
Even if there is a bot/other player building stuff, the widget will not notify on all  their new units if they are outside line of sight.
That is because widgets are limited in what they can "see."
The widget only sees what what the player who is running the widget can see, too. This is to prevent cheating.
=="Hello unit" 2 - with markers and advancement==
change the widget to this and guess what it does:
<code><pre>
function widget:UnitFinished(unitID, unitDefID, teamID)
local x,y,z = Spring.GetUnitPosition (unitID)
Spring.MarkerAddPoint (x,y,z, "a new unit! id=" .. unitID)
end
</pre></code>
It adds a marker point on every new unit!
Note how the unitID parameter from UnitFinished() is passed to GetUnitPosition()
===unitIDs===
Previous paragraph featured ''unitID'' variable.
Every unit, feature or weapon projectile in spring has an ID number:
http://springrts.com/mediawiki/images/3/3c/Units_features_projectiles.png
There are never two units with the same unitID at the same time.
<pre>#YOLO?</pre>
No YOLO: when a unit is destroyed its unitID might be reused later for a different unit.


=a boring counter widget=
local stringOfWords = "BLAH BLAH BLAH"
A counter that is increased when units are made:
local toggle


<code><pre>
function widget:Initialize()
local counter = 0
  Spring.Echo(stringOfWords .. " I STARTED LOOK AT ME JIM!")
function widget:UnitFinished (unitID, unitDefID, teamID)
counter = counter +1
Spring.Echo ("now there are " .. counter .. " units")
if counter > 10 then
Spring.Echo ("wow, more than 10 units!")
end
end
end
</pre></code>


[[TestingYourGame#Lua|Reload the widget]] and note how the counter gets reseted.
That is how widgets roll: Their variables get cleared on reload.


===boring counter widget with tables===
function widget:Update(frame)
Imagine you want a counter that stores for each factory how many units it has already produced. One variable will not be enought here but an array can store multiple things.
  if(toggle == true) then
 
      Spring.Echo("TOCK")
Arrays in Lua are tables. Read about tables: http://lua-users.org/wiki/TablesTutorial
      toggle = false
 
  else
<code><pre>
      Spring.Echo("TICK")
local buildCount = {}
      toggle = true
function widget:UnitFromFactory(unitID, unitDefID, unitTeam, factoryID, factoryDefID, userOrders)
  end
--read from table how many units this factory already built
local n = buildCount[factoryID]
 
--was the entry empty?
if n == nil then
n = 0 --if yes, start counting from 0
end
n = n +1 --increase n by 1 (best comment ever)
Spring.Echo ("This was unit number " .. n .. " from factory " .. factoryID)
--store new buildCount for this factory
buildCount[factoryID] = n
end
end
</pre></code>
UnitFromFactory is basically the same as UnitFinished, but has the usefull ''factoryID'' parameter.
'''Why the if n == nil?'''
In Lua everything not initialized is nil (=nothing) and nil can not be used in any math.
(n = n +1  is a math)
Btw a shorter version could be:
<code><pre>buildCount[factoryID] = (buildCount[factoryID] or 0) + 1</pre></code>
==I made enough "hello world" widgets. What now?==
On [[Lua_Scripting]] the most important are the pages under '''GameState''' and '''Client'''
See these [[TestingYourGame|pro-tips]] to make testing less frustrating.


While widgets are initially easiest to make, soon there is a problem:
widgets are for interface stuff.
That means widgets usually need to either...
* ...draw something on the screen. (for example the health of Commander)
and/or
* ...give orders to units. (for example make Commander automatically run away from danger)
Both graphics and the whole order-system can be bit tricky for starters.
Without graphics and orders, the awesomeness potential of a widget is small.
'''Pro tip:''' gadgets can be awesome without either!
==go go gadgets time!==
We learned widgets can only "look at" the gamestate or do things that players can do, for example ordering units to attack another unit.
On the other hand gadgets are scripts that are able to alter the state of the game.
With gadgets it is possible to implent new game logic like jumpjets, drop pods, tech tree systems, etc.
Unlike widgets, gadgets must be [[Gamedev:Structure#LuaRules.5C|included in a game or in a map.]]
To do so it is best to unpack the game file into an [[Gamedev:Archives|.sdd archive]]
Writing gadgets is similiar to widgets, except the getInfo() header and callin functions are prefixed with <code>gadget:xxx</code> instead of <code>widget:xxx</code>
===synced & unsynced===
Gadgets are split into a synced and into an unsynced part.
In simple terms that means the synced part is usually for controlling the game logic.
The unsynced part is for drawig graphics.
For example look at this lava tide gadget:
http://springrts.com/mediawiki/images/3/33/Beachbuggysuicidecult_lava_sync_unsync.gif
The '''synced''' part is responsible for ''damaging/destroying the units'' in the lava. It actually alters the gamestate.
The '''unsynced''' part is only for ''drawing'' the graphic of the lava. The graphic itself does not influence the gamestate in any way.
In code it looks like this:
<code><pre>
if (gadgetHandler:IsSyncedCode()) then
  --synced code
  ..
  damage all units that are underneath the lava tide level
  ..
else
  --unsynced code
  ..
  drawing the lava with GL-api functions
  ..
end
</pre></code>
</pre></code>
Of course that also means that some functions or callins can only be used in synced or unsynced part. For example the synced part can not draw anything and the unsynced part can not spawn units.
Sometimes it is nessecary to transfer some data/information "across the sync-unsynced border" - but the scope of variables is only in the part they were declared in.
In lava-exampe that might be the current height of lava.
How this still works is explained here: [[LuaTutorials::InterCommunications]]
==Look at more examples==
Common hint for learning spring Lua is: ''"Look at some examples in other games."''
But a spring game usually has dozen of wupdgets and only a few are useful examples for beginners. Many scripts deal with some very specifique problem or are just way too long.
Or one has to be familiar with the game to even understand what the script even does.
Or if one wants to use the wupdget in ones own game, suddendly nothing works anymore because it depends on so many configs or other files.
Two simple examples are:
"Linked units" (hub dies, child-units explode too)
http://springrts.com/phpbb/viewtopic.php?f=23&t=25435
"Moveable metal extractor"
http://springrts.com/phpbb/viewtopic.php?f=14&t=26877
---
==Feedback? Questions?==
This page is open for feedback {{forumlink|topic=32047|title=in the forum.}}
[[category: Lua]]
[[category: Lua]]

Revision as of 18:03, 14 April 2015

Development < Lua Scripting < Lua:Tutorial GettingStarted


function widget:GetInfo()
   return {
      name         = "Name as shown in widget list",
      desc         = "",
      author         = "john doe",
      date         = "date",
      license          = "PD",
      layer          = 0,
      experimental   = false,
      enabled          = true
   }
end

local stringOfWords = "BLAH BLAH BLAH"
local toggle

function widget:Initialize()
   Spring.Echo(stringOfWords .. " I STARTED LOOK AT ME JIM!")
end


function widget:Update(frame)
   if(toggle == true) then
      Spring.Echo("TOCK")
      toggle = false
   else
      Spring.Echo("TICK")
      toggle = true
   end
end