Jump to content
CrazyM

pickinfo.entity name and collision problem with prefabs

Recommended Posts

I'm familiarizing myself with Leadwerks and so I have a simple test map that lets me place block prefabs like Minecraft.

 

How can I retrieve the name from a pickinfo.entity?

 

I tried the following, but it throws an error in Print in the window:MouseDown(1) check: (argument #3 is 'string'; '[no object]' expected.)

function Script:UpdateWorld()
   if block == nil then
       block = Prefab:Load("Prefabs/CM/Block.pfb")
       block:SetKeyValue("name","Block")
       block:SetCollisionType(Collision.Prop)
   end

   local pickinfo = PickInfo()
   local mousePos = window:GetMousePosition()
   if (self.player.script.camera:Pick(mousePos.x, mousePos.y, pickinfo, pickradius, true)) then
       block:SetPosition(Vec3(math.floor(pickinfo.position.x),
           math.floor(pickinfo.position.y),
           math.floor(pickinfo.position.z)) +
           Vec3(pickinfo.normal.x, pickinfo.normal.y/2, pickinfo.normal.z))
   end

   if window:MouseDown(2) then -- Place the block
       block = nil
   end

   if window:MouseDown(1) then -- Delete the block
       if pickinfo ~= nil then
           if pickinfo.entity ~= nil then
               System:Print(pickinfo.entity:GetKeyValue("name"))
           end
           --if pickinfo.entity:GetKeyValue("Name") == "Block" then
           -- pickinfo.entity:Release()
           --end
       end
   end
end

 

The other issue I'm having is that the placed prefabs have no physics collision.

 

Thanks,

Mike

Share this post


Link to post

Let me ask about entity identification another way. If I have a collision with an entity, what is the best way to distinguish the entity from another? For example: instantiated blocks have the name "Block" and the ground the blocks sit on is a CSG with the name "Ground". If I want to Release() blocks, but not the ground, I need to check the collision entity to determine it's a block and to Release it, or if it's the ground and to ignore the collision. Should I use the name of the entity (how do I get this), does Leadwerks have a layer system that I can place items on and check if the collision entity is on that layer, or maybe place a script on the ignore items and check for the type?

 

Thanks

Share this post


Link to post

Given that you can set any old value with setKeyValue why not set a property telling your if it's destructible?

 

:SetKeyValue("canBeMined",true)

 

That way you can also have blocks that cannot be destroyed ( and comparing a bool almost certainly faster than doing string comparision )

Share this post


Link to post

@Aggor - yes the GetKeyValue() with upper case "Name" was a test, I tried upper and lower, they both produce the same error.

 

@Haydenmango - Thanks, I'll try GetClass() and GetClassName() out.

 

@Guppy - This sounds great because it would allow me to categories everything, and in my case I really only need a bool check, but can you spot anything I'm doing wrong with the code above, and why it errors:

 

I instantiate the prefab, then set a key/value pair with name/Block

block = Prefab:Load("Prefabs/CM/Block.pfb")
block:SetKeyValue("name","Block")

 

then, in the pickinfo I'm attempting to read the key/value pair

System:Print(pickinfo.entity:GetKeyValue("name"))

 

But the Print is producing this error: argument #3 is 'string'; '[no object]' expected.

 

Thanks guys!

Share this post


Link to post

Isn't csg blocks collapsed, so they are not really seperate runtime as entities, unless they have mass or have a connected script.

Share this post


Link to post

I'm not too experienced in LUA but I would guess, you didn't define the key-value "name" for the ground and thus the function returning nil for that case and a comparison between nil and a String isn't possible (as said, I don't know about LUA, but I would guess that's the case). So have you tried checking whether it is nil before doing that? Something like:

if pickinfo.entity:GetKeyValue("Name") then --This is the same as a comparison with ~= nil
   if pickinfo.entity:GetKeyValue("Name") == "Block" then
    pickinfo.entity:Release()
   end
end

Share this post


Link to post

Ran head long into much the same problem - I wanted to check if a specific key had NOT been set. In this case you would expect GetKeyValue to return nil - however it always returns a string and in the case of unset values it returns an empty string

So

if entity:GetKeyValue("unsetkey") ~= nil then
System:Print('this will be printed');
end

 

Where as

if entity:GetKeyValue("unsetkey") ~= "" then
System:Print('this wont be printed');
end

 

 

Embarrassingly enough this is actually mentioned in the docs;

http://www.leadwerks.com/werkspace/page/documentation/_/command-reference/entity/entitygetkeyvalue-r773

Share this post


Link to post

@shadmar - After you mentioned it, I started looking deeper into my prefab and the Leadwerks collision rules. I have made a little progress. I swapped my CSG block for a basic blender cube, and I'm now setting SetPickMode(0) and SetCollisionType(4) on the instantiated prefab so that it won't be picked while positioning for placement. I've adjusted my camera picks to use collision type to (2), and after I place a block I set SetPickMode(1) and SetCollisionType(2) so that it should be pickable again. Once a block is placed however, I can collide with it when walking up to it or jumping on top of it with the FPSPlayer, but I still can't camera pick the placed blocks. I have the exact same scenario using the CSG cube prefab after SetPickMode and SetCollisionType adjustments, the collider stops me from walking through it, but I can't pick it from the camera.

 

These pick failure are what I was perceiving as Key/Value failures. If I place an instance of my prefab in the scene at design time, I can pick it at runtime and read the Key/Value, but not when instantiated during runtime. I tried adding world:Update and world:Render() after block placement just in case some physics update was necessary, but it had no effect.

 

So it seems something must be required to commit pickmode and collisiontype changes at runtime, but I can't seem to figure out what.

 

The current state of my script:

Script.player = nil --entity "Player"

window = Window:GetCurrent()
world = World:GetCurrent()
context = Context:GetCurrent()
pickradius = 0.1
block = nil
blockMode = false
pickinfo = nil
mousePos = nil

function Script:Start()
   pickinfo = PickInfo()
end

function Script:UpdateWorld()
   mousePos = window:GetMousePosition()

   -- Toggle blockMode
   if window:KeyHit(Key.ControlKey) then
       if blockMode == false then
           blockMode = true
       else
           blockMode = false
       end
   end

   -- blockMode
   if blockMode == true then
       -- Load a new block
       if block == nil then
           block = Prefab:Load("Prefabs/CM/Cube.pfb")
           block:SetPickMode(0) -- ignore 
           block:SetCollisionType(4) 
           block:SetKeyValue("name","Block") 
       end

       -- Position the block grid aligned and normal offset
       if block ~= nil then
           if (self.player.script.camera:Pick(mousePos.x, mousePos.y, pickinfo, pickradius, true, 2)) then
               block:SetPosition(Vec3(math.floor(pickinfo.position.x), 
                   math.floor(pickinfo.position.y), 
                   math.floor(pickinfo.position.z)) + 
                   Vec3(pickinfo.normal.x, pickinfo.normal.y, pickinfo.normal.z)) 
           end
       end

       -- Place the block
       if window:MouseHit(2) then -- right mouse button
           block:SetPickMode(1) -- sphere
           block:SetCollisionType(2)
           block = nil
       end

       -- Debug
       if pickinfo ~= nil then
           if pickinfo.entity ~= nil then
               System:Print("pickinfo: " .. pickinfo.entity:GetKeyValue("name"))
           end
       end

       -- Release the block
       if window:MouseHit(1) then -- left mouse button
           if (self.player.script.camera:Pick(mousePos.x, mousePos.y, pickinfo, pickradius, true, 2)) then 
               if pickinfo.entity:GetKeyValue("name") == "Block" then
                   pickinfo.entity:Release()
               end
           end
       end
   else -- Not in blockMode, release the block
       if block ~= nil then 
           block:Release()
           block = nil
       end
   end
end

Share this post


Link to post

I finally got everything working to some degree. It could use some more refinement, but this was just an API familiarization exercise for me.

 

Thanks everyone for you input and assistance.

 

Just in case anyone is interested in the basis of a Minecraft style block pick/placement script, here it is.

 

1. Create a one unit (meter?) cube in your favorite 3D app.

2. Import and save as a prefab.

3. Create a pivot, and attached this script to it, then parent the pivot to your FPSPlayer.

4. Link in the player, prefab, a pickup sound, a placement sound, and a texture reticle.

 

Left Ctrl = toggles in and out of block building mode

Right mouse click = places a block

Left mouse click = removes a block

 

Cheers!

 

EDIT:

Updated to fix some issues

 

Script.player = nil --entity "Player"
Script.blockPrefab = "" --path "Prefab" "Prefab (*.pfb):pfb"
Script.pickSound = "" --path "Pick Sound" "Sound File (*.wav):wav;Ogg Vorbis (*.ogg):ogg"
Script.placeSound = "" --path "Place Sound" "Sound File (*.wav):wav;Ogg Vorbis (*.ogg):ogg"
Script.reticle = "" --path "Reticle" "Tex file (*.tex):tex;"

window = Window:GetCurrent()
world = World:GetCurrent()
context = Context:GetCurrent()
pickradius = 0.1
block = nil
blockMode = false
pickinfo = nil
mousePos = nil
pickSnd = nil
placeSnd = nil
reticleOptic = nil


function Script:Start()
   pickinfo = PickInfo()
   pickinfoTest = PickInfo()
   pickSnd = Sound:Load(self.pickSound)
   placeSnd = Sound:Load(self.placeSound)
   reticleOptic = Texture:Load(self.reticle)
end


function Script:UpdateWorld()
   mousePos = window:GetMousePosition()

   -- Toggle blockMode
   if window:KeyHit(Key.ControlKey) then
       if blockMode == false then
           blockMode = true
       else
           blockMode = false
       end
   end

   -- blockMode
   if blockMode == true then
       -- Load a new block
       if block == nil then            
           block = Prefab:Load(self.blockPrefab)    
           block:SetPickMode(0) -- ignore        
           block:SetCollisionType(4)                
           block:SetKeyValue("name","Block")        
       end

       -- Position the block grid aligned and normal offset
       if block ~= nil then
           if (self.player.script.camera:Pick(mousePos.x, mousePos.y, pickinfo, pickradius, true)) then
               local blockPos = Vec3(Math:Round(pickinfo.position.x), 
                   Math:Round(pickinfo.position.y), 
                   Math:Round(pickinfo.position.z))

               block:SetPosition(blockPos, true)            
           end
       end

       -- Place the block
       if window:MouseHit(2) then -- right mouse button
           if placeSnd ~= nil then
               placeSnd:Play()
           end
           block:SetPickMode(2) -- Polygon
           block:SetCollisionType(2)
           block:SetShape(Shape:Box())
           block:SetRotation(Vec3(0,0,0), true)
           block = nil            
       end

       -- Release the block
       if window:MouseHit(1) then -- left mouse button
           if pickinfo.entity:GetKeyValue("name") == "Block" then
               System:Print("Release")
               pickinfo.entity:Release()
               if pickSnd ~= nil then
                   pickSnd:Play()
               end
           end

       end
   else -- Not in blockMode, release the block
       if block ~= nil then    
           block:Release()
           block = nil
       end
   end
end


function Script:PostRender(context)
   if reticleOptic ~= nil then
       context:SetColor(1,1,1,1)
       context:SetBlendMode(Blend.Alpha)        
       context:DrawImage(reticleOptic, context:GetWidth()/2-32, context:GetHeight()/2-32, 64, 64)
       context:SetBlendMode(Blend.Solid)
   end    
end

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...