Core Posted July 14, 2017 Share Posted July 14, 2017 I have a switch that opens a door. When it is used, it also lights up a light in switch panel. It works by changing material to emissive and back to black. This is the script in my door switch: Don't mind variable names "messageshow", I use the same timer for text messages and keeping light lit for certain time. if self.activateLight == true then self.messageshow = true if self.messageshow == true and self.messagetimecount > Time:GetCurrent() then --Activation light on self.activationSurface:SetMaterial(self.activateMaterial) else self.messageshow = false self.activateLight = false self.activationSurface:SetMaterial(self.offMaterial) end end Problem is, that it lights up ALL switch panels I have in the scene. Why? Quote Link to comment Share on other sites More sharing options...
Rick Posted July 14, 2017 Share Posted July 14, 2017 Because materials are shared between model instances to save space. If you use the editor to add models you can't get around this as that's what the editor does. If you can load a model in code you can control this behavior and tell it to use it's own material via the Entity:Copy() function I think? Once of those has an option to use it's own material. Clearly you'd want to place these in the editor so you know where they are, so maybe a workaround would be in it's script do a copy on the entity itself in the Start() function and replace the editor model with your new copy while setting the pos/rot of the new copy unshared material model to the one you placed in the editor and hide/release the editor version. So basically the editor version of the model is just acting as a placeholder for it's position/rotation to the one you load in code. Quote Link to comment Share on other sites More sharing options...
Core Posted July 15, 2017 Author Share Posted July 15, 2017 Ah, I see. Then I need to figure out another way. Your suggestion seems plausible, but lot of work a.d extra code, especially if I want to use lots of interactive emissive lights... What about decals, if I show/hide them instead of switching material? Never used decals before. Quote Link to comment Share on other sites More sharing options...
Rick Posted July 16, 2017 Share Posted July 16, 2017 The copy and swap isn't a lot of code really. You'd attach a script to one of these things and inside its Start function copy self.entity where it's materials aren't shared and then set its position and rotation to what self.entity is and then hide self.entity. Save as prefab and place as many as you want. Might not be the most efficient way though but it's a couple lines of code only. The shader way is probably the most efficient way but more complex. Id it would be nice if josh offered a flag for each model if it's material is shared between instances or not. Quote Link to comment Share on other sites More sharing options...
Core Posted July 18, 2017 Author Share Posted July 18, 2017 Ok, thanks, I will look in to it. Quote Link to comment Share on other sites More sharing options...
Core Posted July 19, 2017 Author Share Posted July 19, 2017 Tried @Rick's suggestion with this simple code (at this point I'm just trying to copy switch panel object), but it crashes the map. What am I missing? local switchPanel = self.entity:Copy() switchPanel:SetPosition(0,0,0) switchPanel:SetRotation(0,0,0) Quote Link to comment Share on other sites More sharing options...
Rick Posted July 19, 2017 Share Posted July 19, 2017 Can you show the entire script? With those three lines what line does the crash happen? Quote Link to comment Share on other sites More sharing options...
Core Posted July 19, 2017 Author Share Posted July 19, 2017 It crashes the very first line. Even if I comment out other two lines. But I realized one thing... I'm using Flowgraph editor. If I copy entity, I guess flow graph editor does not work anymore with the copy? Here is the full script: --UseTimer variables Script.NullVar = nil --choice "Use Timer" "Version: 3, By: Einlander" Script.enabled = true -- bool "Enabled" Script.HoldTime = 7 -- Float "Hold Time" -- How long the use key needs to be held Script.CumulativeTime = false -- bool "Cumulative Time" -- The timer does not reset when the use key is let go Script.ShowGraphics = true -- bool "Show Graphics" -- Draws the completion bar on screen Script.DisableOnComplete= false -- bool "Stop when Done" -- Disables script on completion --Technical variables Script.enabled=true--bool "Enabled" Script.soundfile=""--path "Sound" "Wav Files (*.wav):wav" Script.working = false --bool "Working" Script.repairing = false Script.power = false --bool "Power" Script.activationSurface = nil Script.poweredSurface = nil Script.activateMaterialPath = nil --path "Activate Material" "Material Files (*.mat):mat" Script.activateMaterial = nil Script.poweredMaterialPath = nil --path "Powered Material" "Material Files (*.mat):mat" Script.poweredMaterial = nil Script.offMaterialPath = nil --path "Off Material" "Material Files (*.mat):mat" Script.offMaterial = nil Script.activateLight = false Script.playerInventory = nil --Notifications Script.messagetimecount = 0 Script.text1 = "Replacing Circuitboard" --string "Repair Message" Script.text2 = "Bad Circuitboard" --string "Broken Message" Script.text4 = "Out of Power" --string "No Power Message" Script.text5 = "Switch" --string "Content Msg." Script.messagetime = 1000 --int "Message Time" Script.messageshow = false Script.message2show = true Script.messageoffset = 70 Script.messageoffset2 = 150 Script.smoke = "" --entity "Smoke" --Notifications function Script:Powered(power) --in self.power = true end function Script:Start() --local switchPanel = self.entity:Copy() --switchPanel:SetPosition(0,0,0) --switchPanel:SetRotation(0,0,0) --self.entity:Release() self.totaltime = 0 if self.soundfile then self.sound = Sound:Load(self.soundfile) end self.activationSurface = self.entity:GetSurface(1) self.poweredSurface = self.entity:GetSurface(2) self.offMaterial = Material:Load(self.offMaterialPath) self.poweredMaterial = Material:Load(self.poweredMaterialPath) self.activateMaterial = Material:Load(self.activateMaterialPath) self.activationSurface:SetMaterial(self.offMaterial) self.poweredSurface:SetMaterial(self.offMaterial) end function Script:Use(player) if self.enabled == false then return end--UseTimer code self.playerInventory = player.script:GetInventory() self.messagetimecount = Time:GetCurrent() + self.messagetime if self.working == false then if self.playerInventory.playerItems[1] ~= nil then if self.playerInventory.playerItems[1].name == "Circuitboard" then --System:Print("Using Circuitboard") self:Begin() --UseTimer code end end end if self.power == true then if self.working == true then if self.sound then self.entity:EmitSound(self.sound) end self.component:CallOutputs("Use") self.activateLight = true end end end --UseTimer code function Script:Begin() if self.enabled == false then return end self.started = true self.complete = false self.repairing = true --if (self.totaltime >= (self.HoldTime - 1)*1000) then --self.totaltime = 0 --end if (self.CumulativeTime == false) then self.totaltime = 0 end self.component:CallOutputs("started") end function Script:Complete() if self.enabled == false then return end self.complete = true self.working = true self.repairing = false self.totaltime = 0 self.smoke:SetLoopMode(false, false); self.playerInventory.playerItems[1] = nil if self.sound then self.entity:EmitSound(self.sound) end if (self.DisableOnComplete == true) then self:Disable() end self.component:CallOutputs("complete") --System:Print("complete") end function Script:Enable()--in if self.enabled==false then self.enabled=true self.component:CallOutputs("Enable") self.health=1 end end function Script:Disable()--in if self.enabled then self.enabled=false self.component:CallOutputs("Disable") self.health=0 end end function Script:Release() if self.sound then self.sound:Release() end end function Script:UpdatePhysics() if self.enabled == false then return end if self.complete == true then return end if ((self.started == true) or (self.held == true) or (self.released == true)) == true then if self.totaltime >= ((self.HoldTime-1) * 1000) then self:Complete() end end if self.started == true then if (window:KeyDown(Key.E) == true) then self.started = false self.held = true self.timestart = Time:GetCurrent() return end end if self.held == true then if (window:KeyDown(Key.E) == true) then self.held = true self.timeend = Time:GetCurrent() self.totaltime = self.totaltime + (self.timeend - self.timestart) self.timestart = Time:GetCurrent() self.component:CallOutputs("running") self.timeend = Time:GetCurrent() else self.held=false self.released = true self.timeend = Time:GetCurrent() self.totaltime = self.totaltime + (self.timeend - self.timestart) self.timestart = Time:GetCurrent() end return end if self.released == true then self.timeend = Time:GetCurrent() self.totaltime = self.totaltime + (self.timeend - self.timestart) self.released = false return end if self.activateLight == true then self.messageshow = true if self.messageshow == true and self.messagetimecount > Time:GetCurrent() then --Activation light self.activationSurface:SetMaterial(self.activateMaterial) else self.messageshow = false self.activateLight = false self.activationSurface:SetMaterial(self.offMaterial) end end if self.power == true and self.working == true then self.poweredSurface:SetMaterial(self.poweredMaterial) else self.poweredSurface:SetMaterial(self.offMaterial) end end function Script:WritePercentage(number) --in System:Print(tostring(number)) end --[[ Description: Calculates Percent Complete Parameters: none Notes: Has a flowgraph ARGUMENT that sends out the percentage completed ]] function Script:Percentage() --arg return math.floor((self.totaltime / ((self.HoldTime-1)*1000))*100) end function Script:PostRender(context) context:SetBlendMode(Blend.Alpha) context:SetFont(font2) context:SetColor(hudColorSolid) if self.working == false then if self.repairing == true and self.held == true then --show notification at center of the screen local X = math.floor((context:GetWidth() - font2:GetTextWidth(self.text1)))/2 local Y = math.floor((context:GetHeight() - (font2:GetHeight()-self.messageoffset)))/2 context:DrawText(self.text1, X, Y) else self.messageshow = true if self.messageshow == true and self.messagetimecount > Time:GetCurrent() then --show notification at center of the screen local X = math.floor((context:GetWidth() - font2:GetTextWidth(self.text2)))/2 local Y = math.floor((context:GetHeight() - (font2:GetHeight()-self.messageoffset)))/2 context:SetColor(hudErrorColor) context:DrawText(self.text2, X, Y) else self.messageshow = false end end end if self.power == false and self.repairing == false then self.messageshow = true if self.messageshow == true and self.messagetimecount > Time:GetCurrent() then --show notification at center of the screen local X = math.floor((context:GetWidth() - font2:GetTextWidth(self.text4)))/2 local Y = math.floor((context:GetHeight() - (font2:GetHeight()-self.messageoffset2)))/2 context:SetColor(hudErrorColor) context:DrawText(self.text4, X, Y) end self.messageshow = false end if self.enabled == false then return end if self.complete == true then return end if ((self.started == true) or (self.held == true) or (self.released == true)) == false then return end context:SetBlendMode(Blend.Alpha) window = Window:GetCurrent() local segment = Vec2(window:GetWidth() *.45,window:GetHeight() *.49) context:SetColor(hudBackColor) context:DrawRect(segment.x-8, segment.y+55 , window:GetWidth() - (segment.x*2),window:GetHeight() - (segment.y*2)) context:SetColor(hudColorSolid) context:DrawRect(segment.x-4, segment.y+4+55 , (window:GetWidth() - ((segment.x+4)*2))*(self:Percentage()*.01) , window:GetHeight() - ((segment.y+4)*2)) end Quote Link to comment Share on other sites More sharing options...
Rick Posted July 19, 2017 Share Posted July 19, 2017 What's the error message you get? Quote Link to comment Share on other sites More sharing options...
Core Posted July 19, 2017 Author Share Posted July 19, 2017 Nothing, just crash. The usual windows error "Program is not responding and yada yada..." Quote Link to comment Share on other sites More sharing options...
Rick Posted July 19, 2017 Share Posted July 19, 2017 It might be in an infinite loop. Since this entity has a script attached that copies itself inside Start() it's copy might be calling Start as well making another copy rinse and repeat. Put a System:Print() at the start of Start() to see if s bunch show up. if that's the case there are ways around this. Quote Link to comment Share on other sites More sharing options...
macklebee Posted July 19, 2017 Share Posted July 19, 2017 On 7/14/2017 at 3:17 PM, Core said: I have a switch that opens a door. When it is used, it also lights up a light in switch panel. It works by changing material to emissive and back to black. Problem is, that it lights up ALL switch panels I have in the scene. Why? The workaround in LE2 for a simple ON/OFF switch with an emissive material was to just change the color of the model which via shader would change the texture for the emissive output. Just for fun, I converted this setup to LE4, and gave it a modified simple pushbutton script that uses the switch sound ("switch.lua") and a shader that works in LE4 ("switch.shader"). Hope it helps. LE2switch.zip 1 Quote Win7 64bit / Intel i7-2600 CPU @ 3.9 GHz / 16 GB DDR3 / NVIDIA GeForce GTX 590 LE / 3DWS / BMX / Hexagon macklebee's channel Link to comment Share on other sites More sharing options...
Rick Posted July 20, 2017 Share Posted July 20, 2017 Shaders always come to the rescue if you know how to use them Quote Link to comment Share on other sites More sharing options...
GorzenDev Posted July 20, 2017 Share Posted July 20, 2017 move certain variable declarations inside the start() function. since declaring Script.variablename will make them shared across all instances of the script. Quote Link to comment Share on other sites More sharing options...
Rick Posted July 20, 2017 Share Posted July 20, 2017 21 minutes ago, GorzenDev said: move certain variable declarations inside the start() function. since declaring Script.variablename will make them shared across all instances of the script. That's only if the variable is a table. Quote Link to comment Share on other sites More sharing options...
AggrorJorn Posted July 20, 2017 Share Posted July 20, 2017 I got tricked by this several times. --Common variables like strings, numbers and bool Script.test1 = "test1" -- Variable has a unique instance per script local test2 = "test2" --Variable is shared between all instances of the script test3 = "test3" --Global, used by everone and everything (insert 'your mother..' joke) --Tables Script.table1 = {} --Shared between all instances of the script Script.table2 = nil --Declare in start function Script:Start() self.table2 = {} --Table has a unique instance per script end 1 Quote Link to comment Share on other sites More sharing options...
GorzenDev Posted July 20, 2017 Share Posted July 20, 2017 24 minutes ago, Rick said: That's only if the variable is a table. in this case his material does to since his material is set through the editor. creating a material in the start() function will fix this like you suggested in your first post. Quote Link to comment Share on other sites More sharing options...
Rick Posted July 20, 2017 Share Posted July 20, 2017 All instances of a model placed in the editor share the material no matter how it's set. So it doesn't matter where or how he sets the material for this model, whenever he set it's for 1 all instances will automatically change as well. The same model placed x times in the editor are setup to share the same material. To get different materials for the same model you either have to make it in code because then you can control that setting or use an atlas shader that mimic different textures. For an atlas shader it still uses the same material but per instance you can control what part of the atlas texture to use which results in different textures on that model. In this case he can just use the shader Mack provided since he's not really looking for completely different textures. If you had a character and wanted them to seem like they are wearing different clothes or textures then an atlast shader would be one way to go vs making copies as you'd at least save on the model memory and speed of loading if loading a new one in real-time since they are instanced vs Copy() which reloads a new instance which takes longer and can't be used in real-time. Quote Link to comment Share on other sites More sharing options...
GorzenDev Posted July 20, 2017 Share Posted July 20, 2017 3 minutes ago, Rick said: All instances of a model placed in the editor share the material no matter how it's set. So it doesn't matter where or how he sets the material for this model, whenever he set it's for 1 all instances will automatically change as well. The same model placed x times in the editor are setup to share the same material. i am not saying you are wrong in anything you said. sorry i should have explained it proper in my first post. personaly i would not have created theses materials in the editor at all but rather load the texture and create a new material for every instance of the script that runs the start() function as AggrorJon points out nicely. Quote Link to comment Share on other sites More sharing options...
Rick Posted July 20, 2017 Share Posted July 20, 2017 But creating unique instances of the material won't matter. It's the model and all instances of it that points to the last material that's assigned to it. When you set any material on an instance of a model all instances will get that material. It's the model that is saying all instances of myself will point to the last assigned material on me. That's why you have to copy the model or load it at runtime with the unmanaged flag to make that model not share materials. So simply loading unique materials in start won't give the desired effect since all of those models are still instances when placed via the editor. In that case whatever script was last to run will set the material and all instances of that model will now be sharing that material. Quote Link to comment Share on other sites More sharing options...
Core Posted July 20, 2017 Author Share Posted July 20, 2017 Thanks for the help and discussion and @macklebee for the shader example! Awesome. I'm away from computer for the next few days, I'll continue after that. Quote Link to comment Share on other sites More sharing options...
macklebee Posted July 20, 2017 Share Posted July 20, 2017 8 hours ago, Rick said: But creating unique instances of the material won't matter. It's the model and all instances of it that points to the last material that's assigned to it. When you set any material on an instance of a model all instances will get that material. It's the model that is saying all instances of myself will point to the last assigned material on me. That's why you have to copy the model or load it at runtime with the unmanaged flag to make that model not share materials. So simply loading unique materials in start won't give the desired effect since all of those models are still instances when placed via the editor. In that case whatever script was last to run will set the material and all instances of that model will now be sharing that material. Yeppers. Basically any item that is an asset (model,material,texture,etc) will need to be created, loaded as un-managed, or copied to prevent it from affecting any other previous instance of that asset. Has to be done via code as currently no way to load unique copies in editor. Other discussions about this: 1 hour ago, Core said: Thanks for the help and discussion and @macklebee for the shader example! Awesome. I'm away from computer for the next few days, I'll continue after that. Glad to help. I was just going to link to the LE2 switch converted to LE4 in the workshop or forum but was surprised when I couldn't find it. This switch was the convenient workaround for the same problem of instances in the Editor back in the LE2 days. Quote Win7 64bit / Intel i7-2600 CPU @ 3.9 GHz / 16 GB DDR3 / NVIDIA GeForce GTX 590 LE / 3DWS / BMX / Hexagon macklebee's channel Link to comment Share on other sites More sharing options...
Core Posted September 21, 2017 Author Share Posted September 21, 2017 Finally had time to get back to this one. I tried macklebees example and it worked like a charm, but I had actually no idea how it worked... So I got back to Ricks copying suggestion. And decided to create parent for my switch with copying script, copy manager if you will, to prevent it duplicating it self forever. It actually worked, but it lost all the flowgraph attachments, naturally, because it was not the same entity anymore. Here is the code I came up to create unique entity with unique materials. Script.createCopy = false Script.originalModel = nil Script.finalModel = nil function Script:Start() if self.createCopy == false then self.originalModel = self.entity:FindChild("SwitchPanel1_5") self.finalModel = tolua.cast(self.originalModel:Copy(), "Entity") self.originalModel:Hide() self.createCopy = true end end So next step was to move all my switch code to parent, and create new script for 3d model, with functions including material changing code. And call those functions from parent entitys script when I needed materials to change. And so finally, it does what it supposed to do! Thank you all for your help! 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.