Reading key events from irrlua

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
elander
Posts: 193
Joined: Tue Oct 05, 2004 11:37 am

Reading key events from irrlua

Post by elander »

Could someone explain how to read key presses with irrlua? The gui example is slightly different than the c++ example. It uses a OnGuiEvent and a strange iir.createIEventReceiver method that doesn't exist in the c++ version.

Edit: Ok i found some documentation on the net.

But this means that we can't use the OnEvent method anymore with an event handler?

And is it possible to do this?

myEventHandler = irr.createIEventReceiver(myEventHandler)
Guest

Post by Guest »

But this means that we can't use the OnEvent method anymore with an event handler?
To read keypresses you should use OnKeyEvent (Key, PressedDown, Shift, Control)... OnGuiEvent deals with gui controls and windows
The file 04.Movement.lua has it explained :D
And is it possible to do this?

myEventHandler = irr.createIEventReceiver(myEventHandler)
Not only possible, but what should be done! :wink:
elander
Posts: 193
Joined: Tue Oct 05, 2004 11:37 am

Post by elander »

OK thanks i have it working. But now im not reading mouse input. Whenever i move the mouse inside the window it should receive mouse motion events. Must i do something before that to read mouse events?

Heres the lua code:

Code: Select all

require "IrrLua"

-- game global state
SFglobal = {}

-- global data path
SFglobal.dataPath = "../Data"

-- irrlicht related state
SFglobal.irr = {}
SFglobal.irr.device = {}
SFglobal.irr.eventHandler = {}

-- default IrrLua event handlers
function SFglobal.irr.eventHandler:OnKeyEvent(Key, PressedDown, Shift, Control)
    print("keyboard event received ", Key, PressedDown)
    SFglobal.gui.ship1.x = SFglobal.gui.ship1.x + 1
    return true
end

function SFglobal.irr.eventHandler:OnMouseEvent(Event, X, Y, Wheel)
    print("Mouse event received ", X, Y)
    return false
end

function SFglobal.irr.eventHandler:OnGuiEvent(Event, Caller)
	return false
end

function SFglobal.irr.eventHandler:OnLogTextEvent(Event, Text)
	return false
end

function SFglobal.irr.eventHandler:OnUserEvent(UserData1, UserData2, UserData3)
	return false
end


function SFinit(width, height, depth, fullscreen)
    SFglobal.irr.device = irr.createDevice(irr.video.EDT_DIRECT3D8, 
                              irr.core.dimension2d(width, height), depth, 
                              fullscreen, false, false)
    local device = SFglobal.irr.device
    if device == nil then return 0 end
    SFglobal.irr.eventHandler = irr.createIEventReceiver(SFglobal.irr.eventHandler)
    device:setEventReceiver(SFglobal.irr.eventHandler)
    return 1
end

function SFquit()
    SFglobal.irr.device:drop()
    return 0
end

function SFinitGUI()
    local dataPath = SFglobal.dataPath
    local device = SFglobal.irr.device
    local driver = device:getVideoDriver();
    local guienv = device:getGUIEnvironment();

    -- load fonts

    SFglobal.gui = {}
    SFglobal.gui.default = {}
    SFglobal.gui.default.fonts = {}
    local fontPath = SFglobal.dataPath .. "/GUI/Style/Default/f"
    local fonts = {}
    fonts.builtin = guienv:getBuiltInFont()
    fonts.fixedfont = guienv:getFont(fontPath .. "/SF_FixedFont-10px.png")
    fonts.smallfont = guienv:getFont(fontPath .. "/SF_SmallFont-10px.png")
    SFglobal.gui.default.fonts = fonts
    
    -- load two space ships for testing purposes
    
    SFglobal.gui.ship1 = {}
    SFglobal.gui.ship1.i = driver:getTexture(SFglobal.dataPath .. "/Entity/Ship/Arth/i/challenger.png")
    SFglobal.gui.ship1.r = irr.core.rect(0,0,64,64)
    SFglobal.gui.ship1.x = 100
    SFglobal.gui.ship1.y = 100
    SFglobal.gui.ship2 = {}
    SFglobal.gui.ship2.i = driver:getTexture(SFglobal.dataPath .. "/Entity/Ship/Arth/i/wyltakin.png")
    SFglobal.gui.ship2.r = irr.core.rect(0,0,64,64)
    SFglobal.gui.ship2.x = 150
    SFglobal.gui.ship2.y = 100
end

function SFdrawSprite(sprite)
    local device = SFglobal.irr.device
    local driver = device:getVideoDriver();
    driver:draw2DImage(sprite.i, irr.core.position2d(sprite.x,sprite.y), sprite.r, nil, 
                       irr.video.SColor(255,255,255,255), true)
end

function SFdrawText(font,text,x,y,w,h)
    local device = SFglobal.irr.device
    local driver = device:getVideoDriver();
    local guienv = device:getGUIEnvironment();
    font:draw(text, irr.core.rect(x,y,x+w,y+h), irr.video.SColor(255,255,255,255))
end

function SFdisplay()
    local dataPath = SFglobal.dataPath
    local device = SFglobal.irr.device
    local driver = device:getVideoDriver();
    local guienv = device:getGUIEnvironment();
    local scnmgr = device:getSceneManager();
    local time = 0

    while device:run() do
        driver:beginScene(true, true, irr.video.SColor(0,0,0,0))

        local fonts = SFglobal.gui.default.fonts
        SFdrawText(fonts.fixedfont, "Hello World", 0, 0, 128, 16)
        SFdrawSprite(SFglobal.gui.ship1)
        SFdrawSprite(SFglobal.gui.ship2)

        scnmgr:drawAll()
        guienv:drawAll()

        driver:endScene()
    end
end

SFinit(800,600,32,false)
SFinitGUI()
SFdisplay()
SFquit()
return 0
ElementoY
Posts: 15
Joined: Sun Feb 05, 2006 3:40 am
Location: Brazil
Contact:

Post by ElementoY »

It was me the guest up there, been wandering around this forum for a while before registering :P

I just tried OnMouseEvent, it does nothing. Looking at IrrLua sourcecode I've seen it uses instead OnMouseInputEvent, it worked :D I think I'll leave the author a message for him to fix it in the documentation.
elander
Posts: 193
Joined: Tue Oct 05, 2004 11:37 am

Post by elander »

Hey thanks. I should have a look into the source code next time.
zenaku
Posts: 212
Joined: Tue Jun 07, 2005 11:23 pm

Post by zenaku »

Sorry about all that! :) I'll fix the docs...

The problem with using the event receiver the way Irrlicht does is that initially I simply could not get c++ unions to work with tolua++. SEvent is a union, and it wouldn't show up in the Lua side.


They do work now, but I haven't changed it to the way Irrlicht does event receivers for a couple reasons:

1. Lua does not have a switch statement. In C++ Irrlicht examples, how much do you see:

Code: Select all

switch(Event.EventType)
{
     case EET_GUI_EVENT:
     s32 id = Event.GuiEvent.Caller->getID();
     switch(id)
     { 
           case 23:
           case 24:
           ...
     } 
     
     break;
     case EET_MOUSE_INPUT_EVENT:
     ...
     break;
}



When you start to port nested switch statements from C++ to Lua, it gets really messy with if-then-else everywhere.

2. There would be a small performance hit due to the need to create a table for each event received. It's a small hit though and not really a good argument.



Should I change it back to the way irrlicht does it in the next release and avoid some confusion?
ElementoY
Posts: 15
Joined: Sun Feb 05, 2006 3:40 am
Location: Brazil
Contact:

Post by ElementoY »

zenaku wrote:They do work now, but I haven't changed it to the way Irrlicht does event receivers for a couple reasons:
zenaku wrote: Should I change it back to the way irrlicht does it in the next release and avoid some confusion?
I actually like the way it is done now, makes it a bit simpler to do something like this:

Code: Select all

EventReceiver = {}
EventReceiver.ev = {}
EventReceiver.key = {}

-- Adds Events to the GUI event table
function EventReceiver:GUIAddEvent(id, event, func)
    if EventReceiver.ev[id] == nil then EventReceiver.ev[id]={}  end
    EventReceiver.ev[id][event]=func
end

-- Calls the GUI event if it is in the ev table
function EventReceiver:OnGuiEvent(Event, Caller)
   local id = Caller:getID()
   if EventReceiver.ev[id] then
    if EventReceiver.ev[id][Event] then
	  return EventReceiver.ev[id][Event](Caller)
    end
   end
   return false
end

-- Adds functions to the key event tables
function EventReceiver:KeyAddEvent(Key, Down, Shift, Control, func)
 local d = 0
 local s = 0
 local c = 0
 if Down then d = 1 end
 if Shift then s = 1 end
 if Control then c = 1 end
 if EventReceiver.key[Key] == nil then EventReceiver.key[Key]={} end
 if EventReceiver.key[Key][d] == nil then EventReceiver.key[Key][d]={} end
 if EventReceiver.key[Key][d][s] == nil then EventReceiver.key[Key][d][s]={} end
 EventReceiver.key[Key][d][s][c]=func
end

-- Calls key events
function EventReceiver:OnKeyEvent(Key, Down, Shift, Control)
 local d = 0
 local s = 0
 local c = 0
 if Down then d = 1 end
 if Shift then s = 1 end
 if Control then c = 1 end
 if EventReceiver.key[Key] then
  if EventReceiver.key[Key][d] then
   if EventReceiver.key[Key][d][s] then
    if EventReceiver.key[Key][d][s][c] then return EventReceiver.key[Key][d][s][c]() end
   end
  end
 end
 return false
end
and then to use it, after registering the event receiver:

Code: Select all

EventReceiver:KeyAddEvent(irr.KEY_KEY_W, true, false, false, function() player.speed = player.speed+1 end)

gui:addButton(irr.core.rect(10,10,100,50), nil, 101, "Exit")
EventReceiver:GUIAddEvent(101,irr.gui.EGET_BUTTON_CLICKED, function() device:closeDevice() return true end )
I guess you could do both OnKey/MouseInput/Gui/...Event and OnEvent, I don't think the hit would be big, as events don't happen so frequently in normal apps, I think.
Anyway, thanks for IrrLua, zenaku! It's been saving me a lot of work in my scholarship project at univ.!
elander
Posts: 193
Joined: Tue Oct 05, 2004 11:37 am

Post by elander »

zenaku wrote:Should I change it back to the way irrlicht does it in the next release and avoid some confusion?
I don't have any problem with the new method. It even makes life easier. But just for completeness it may be good to have the two methods.
zenaku
Posts: 212
Joined: Tue Jun 07, 2005 11:23 pm

Post by zenaku »

And is it possible to do this?

myEventHandler = irr.createIEventReceiver(myEventHandler)
---
Not only possible, but what should be done! :wink:
Actually the docs were incorrect (although the irrlua sample source code is right).

The docs had:

Code: Select all


local myNode = irr.scene.createISceneNode(smgr:getRootSceneNode(), smgr, -1, myNode)


It should have said:

Code: Select all


local myNode = irr.scene.createISceneNode(smgr:getRootSceneNode(), smgr, -1, CMySceneNode) 

Notice the last param is the class name definintion, not 'myNode' ! :)

So in your case you would do:

Code: Select all


CMyEventReveiver = {}
function CMyEventReceiver:OnMouseInputEvent(Event, X, Y, Wheel)

    if Event == irr.EMIE_LEFT_MOUSE_DOWN then
         -- do some stuff
         return true
    end

    return false
end

function main()
    local receiver = irr.createIEventReceiver(CMyEventReceiver)
    local device = irr.createDevice(irr.EDT_OPENGL, irr.core.dimension2d(640,480), 32, false, false, false, receiver)
  
    -- or you could also do
    -- local device = irr.createDevice(irr.EDT_OPENGL)
    -- device:setEventReceiver(receiver)

end
Hope this helps, and I've fixed the docs.
ElementoY
Posts: 15
Joined: Sun Feb 05, 2006 3:40 am
Location: Brazil
Contact:

Post by ElementoY »

zenaku wrote: Actually the docs were incorrect (although the irrlua sample source code is right).

The docs had:

Code: Select all


local myNode = irr.scene.createISceneNode(smgr:getRootSceneNode(), smgr, -1, myNode)

I see... I haven't had any error using the same table in the arguments and at the assignment, but that might be because one doesn't usually use more than one event receiver at a time :roll:
zenaku
Posts: 212
Joined: Tue Jun 07, 2005 11:23 pm

Post by zenaku »

ElementoY wrote:
zenaku wrote: Actually the docs were incorrect (although the irrlua sample source code is right).

The docs had:

Code: Select all


local myNode = irr.scene.createISceneNode(smgr:getRootSceneNode(), smgr, -1, myNode)

I see... I haven't had any error using the same table in the arguments and at the assignment, but that might be because one doesn't usually use more than one event receiver at a time :roll:
You *could* do it either way, but you would have to assign the functions after you create the variable. Example:

Code: Select all


-- function to create new nodes
function createNode()

   local myNode = irr.scene.createISceneNode(sgmr:getRootSceneNode(), smgr, -1, myNode)
   --[[
       you could even do:
   
       local myNode = irr.scene.createISceneNode(sgmr:getRootSceneNode(), smgr, -1, {}) -- pass in empty table
    --]]

   myNode.Box = irr.core.aabbox3d(-1,-1,-1,1,1,1)
   myNode.render = function() 
      local driver = SceneManager.getDriver() 
      driver:draw2DImage(...)
   end

   myNode.getBoundingBox = function()
      return self.Box
   end

   return myNode
end


That's not really the way it was set up to be used though. If it works any other way I'm not sure how it's doing it :)
ElementoY
Posts: 15
Joined: Sun Feb 05, 2006 3:40 am
Location: Brazil
Contact:

Post by ElementoY »

zenaku wrote:
ElementoY wrote:
zenaku wrote: Actually the docs were incorrect (although the irrlua sample source code is right).

The docs had:

Code: Select all


local myNode = irr.scene.createISceneNode(smgr:getRootSceneNode(), smgr, -1, myNode)

I see... I haven't had any error using the same table in the arguments and at the assignment, but that might be because one doesn't usually use more than one event receiver at a time :roll:
You *could* do it either way, but you would have to assign the functions after you create the variable.
Oops... I misread that code, I thought you were talking about event receivers... in that case yes, it doesn't hold the table contents :oops: my bad!
elander
Posts: 193
Joined: Tue Oct 05, 2004 11:37 am

Post by elander »

I have another problem now. Im trying to use the xml classes and functions to read a game xml resource file. When i try to run the function:

irr.io.createIrrXMLReader

it says im atempting to call a nil field. This means that this function is not yet binded right?

If this is the case i can allways use lua scripts to take the place of xml resources but i would prefer to use Irrlicht xml api.

This is the code im using:

Code: Select all

require "IrrLua"

-- game global state
SFglobal = {}

-- global data path
SFglobal.dataPath = "../Data"

function SFgetXMLResource(filename)
    print("--> start reading xml <--")
    local dataPath = SFglobal.dataPath
    print("Calling getXMLResource(", filename, ")")

    ------------- PROBLEM --------------------
    local xmlr = irr.io.createIrrXMLReader(filename)

    if (xmlr == nil) then return nil end
    -- allowed states 
    -- START, STOP, SHIP, STAT, ANIM, SPRITE
    local state = "START"
    local resource = {}
    while xmlr:read() do
        local nt = xmlr:getNodeType()
        if nt == irr.io.EXN_NONE then
            print("None: ")
        elseif nt == irr.io.EXN_ELEMENT then
            print("Element: ", xmlr:getNodeName())
        elseif nt == irr.io.EXN_ELEMENT_END then
            print("ElementEnd: ", xmlr:getNodeName())
        elseif nt == irr.io.EXN_TEXT then
            print("Text: ", xmlr:getNodeData())
        elseif nt == irr.io.EXN_COMMENT then
            print("Comment: ")
        elseif nt == irr.io.EXN_CDATA then
            print("Cdata: ", xmlr:getNodeData())
        elseif nt == irr.io.EXN_UNKNOWN then
        else
        end
    end
    xmlr:drop()
    return resource
end


function SFgetResourceInstance(resource)
end

SFgetXMLResource(SFglobal.dataPath .. "/Entity/Ship/Numlox/NumloxScout.xml")
When i run this script i get the result:

Code: Select all

Calling getXMLResource( ../Data/Entity/Ship/Numlox/NumloxScout.xml      )

lua: sf3_irrlicht_plug.lua:63: attempt to call field `createIrrXMLReader' (a nil
 value)
stack traceback:
        sf3_irrlicht_plug.lua:63: in function `SFgetXMLResource'
        sf3_irrlicht_plug.lua:178: in main chunk
        [C]: ?
zenaku
Posts: 212
Joined: Tue Jun 07, 2005 11:23 pm

Post by zenaku »

IrrXMLReader is currently unsupported.

IXMLReader works, however. See the example 09.MeshViewer.lua for details on how to use it.

Example:

Code: Select all

local xml = device:getFileSystem():createXMLReader(filename)

These classes are currently not supported:

line2d
heapsort
IFileList
IReadFile
irrMath
irrString
IStringParameters
IWriteFile
irrList
irrXML



Irrlicht has IrrXMLReader, IrrXMLWriter, IXMLReader, and IXMLWriter. The 'I' versions should work. The 'Irr' versions use nested templates however. tolua++ supports templates, but I can't seem to get nested templates to work right.
elander
Posts: 193
Joined: Tue Oct 05, 2004 11:37 am

Post by elander »

Thanks again.
Post Reply