Jump to content

Flashlight battery tutorial


thehankinator
 Share

Recommended Posts

This is a short tutorial to add a battery to the default FPSPlayer.lua script. I am going to assume you are familiar with lua script and Leadwerks 3.5 so I wont be spending much time explaining how to place entities on a map etc. The code samples below will have the new code surrounded by comments, the rest is there just to give you reference to what section of the code we are working in.

 

I broke this problem down into 3 separate aspects.

  1. Flashlight logic
     
  2. Battery level indication (HUD)
     
  3. Battery pickup

Initial Setup

Open your project or create a new one. In the asset tab, navigate to the FPSPlayer.lua script. Make a copy of it, call it FPSPlayerBattery.lua. Next at the top of FPSPlayerBattery.lua, we need to add the variables that we will need to make this happen.

 

Script.mouseDifference = Vec2(0,0)
Script.playerMovement = Vec3(0,0,0)
Script.tempJumpForce = 0

--+++BATTERY VARIABLES BEGIN+++
Script.battery_level = 100 --float "Battery level"
Script.battery_max_level = 100 --float "Battery max level"
Script.battery_discharge_rate = 0.5 --float "Battery discharge rate"
--+++BATTERY VARIABLES END+++

function Script:CycleWeapon(direction)
local n,weapon
local foundindex=false

 

Script.battery_level

The current level. In this example I made 100 the current. If you wanted the player to start with no battery power, you could change this to 0.

 

Script.battery_max_level

The max level of the battery. If the player doesn't have a battery, you could set this to 0 or if they got an upgraded battery bump this value up.

 

Script.battery_discharge_rate

The speed which the battery will die while the light is on.

 

Flashlight logic

This will occur in the UpdatePhysics function of FPSPlayerBattery.lua.

 

--Update the footstep sounds when walking
self:UpdateFootsteps()

--+++BATTERY LOGIC BEGIN+++
--Toggle the flash light on and off
if window:KeyHit(Key.F) then
if self.flashlight:Hidden() then --check if the flashlight is off
if self.battery_level > 0 then --check if we battery power
self.sound.flashlight:Play() --play the sound
self.flashlight:Show() -- turn on the light
end
else --light is already on
self.sound.flashlight:Play() --play the sound
self.flashlight:Hide() --turn off the light
end
end

if not self.flashlight:Hidden() then --check if the light is on
self.battery_level = self.battery_level - self.battery_discharge_rate * Time:GetSpeed() --light is on, reduce the battery level.

if self.battery_level <= 0 then --check if the battery died
self.battery_level = 0 --just incase the battery level went below 0, reset it to 0
self.sound.flashlight:Play() --play the usual sound
self.flashlight:Hide() --turn off the light
end
end
--+++BATTERY LOGIC END+++

--Apply forces to make the carried object move the way we want
if self.carryingEntity then

 

Battery level indication (HUD)

This will be a bar that will have a green color if you have power, red if you get low. We need to look in the PostRender function of FPSPlayerBattery.lua.

 

context:SetBlendMode(1)
context:SetColor(0,0,0,0.5)
local indent=8
local w = 180
local h = 40

--+++BATTERY INDICATOR BEGIN+++
local battery_percent = self.battery_level / self.battery_max_level --calculate the percentage of battery power remaining
context:SetColor( 1 - battery_percent, battery_percent, 0, 1) --as the battery dies the red component increases while the green decreases
context:DrawRect( 50, 50, 100 * battery_percent, 20) --rectangle width is dependent on the percentage of battery remaining. Wider with more power.
--+++BATTERY INDICATOR END+++
end

 

Battery pickup

In the assets tab, navigate to Scripts/Objects/Items. Create a new script called PickupBattery.lua. Open that sucker up and delete the contents.

 

Script.battery_power = 50 --int "Battery power"
function Script:Collision(entity, position, normal, speed)
local p = entity.script --for convenience

if p.battery_level ~= nil and p.battery_max_level ~= nil then --make sure what collided has the required battery variables
if p.battery_level < p.battery_max_level then --make sure that the battery isn't already full
p.battery_level = p.battery_level + self.battery_power --add the power to the battery
if p.battery_level > p.battery_max_level then --check to see if we over filled the battery
p.battery_level = p.battery_max_level --set the battery to max value
end
self.entity:Release() --get rid of the pickup now that the power was added
end
end
end

 

Script.battery_power

How much power this pickup will give the player

 

Wrap up

That is all the parts needed. Set your player entity to use FPSPlayerBattery.lua. Set the script on the pickups in your level to PickupBattery.lua.

 

I've not decided if I will upload the completed scripts, I don't want spoil the fun of doing it yourself wink.png. Feedback welcome.

  • Upvote 4
Link to comment
Share on other sites

 

What part of it is throwing you off?

 

I went ahead and changed the battery pickup script to this:

 

Script.battery_power = 50 --int "Battery power"
function Script:Use(person)
if  person.script.battery_level ~= nil and person.script.battery_max_level ~=nil then
if  person.script.battery_level < person.script.battery_max_level then
person.script.battery_level = person.script.battery_level + self.battery_power
if person.script.battery_level > person.script.battery_max_level then
person.script.battery_level = person.script.battery_max_level
end
self.entity:Release()
end
end
end

 

And I also changed something in my FPSPlayer script this is around line 445

if usableentity~=nil then

 --Use the object, whatever it may be
 usableentity.script:Use(self.entity)

 

I changed the "Use(self)" to "Use(self.entity)" and this makes it so that the flashlight pickup has to be actually picked up, aka, pressing E on it.

Link to comment
Share on other sites

I think some of these features would be best to be broken out into their own classes. Flashlight seems like a prime candidate for this. The FPS player script is already a big mess. Plus integration/distribution/understanding/modification will be much easier.

 

I think a great deal of things in the FPS player script could benefit from this and be much easier to manage and read.

  • Upvote 1
Link to comment
Share on other sites

I went ahead and changed the battery pickup script to this:

 

Script.battery_power = 50 --int "Battery power"
function Script:Use(person)
if person.script.battery_level ~= nil and person.script.battery_max_level ~=nil then
if person.script.battery_level < person.script.battery_max_level then
person.script.battery_level = person.script.battery_level + self.battery_power
if person.script.battery_level > person.script.battery_max_level then
person.script.battery_level = person.script.battery_max_level
end
self.entity:Release()
end
end
end

 

And I also changed something in my FPSPlayer script this is around line 445

if usableentity~=nil then

 --Use the object, whatever it may be
 usableentity.script:Use(self.entity)

 

I changed the "Use(self)" to "Use(self.entity)" and this makes it so that the flashlight pickup has to be actually picked up, aka, pressing E on it.

 

That works for this pickup but I would refrain from making that sort of change to the FPSPlayer script. It could break some other item's Use function because they would be expecting the script object rather than an entity object. I would change FPSPlayer script back to

if usableentity~=nil then

 --Use the object, whatever it may be
 usableentity.script:Use(self)

 

Then change the pickup script to

Script.battery_power = 50 --int "Battery power"

function Script:Use(person)
if person.battery_level ~= nil and person.battery_max_level ~=nil then
if person.battery_level < person.battery_max_level then
person.battery_level = person.battery_level + self.battery_power
if person.battery_level > person.battery_max_level then
person.battery_level = person.battery_max_level
end
self.entity:Release()
end
end
end

Link to comment
Share on other sites

That works for this pickup but I would refrain from making that sort of change to the FPSPlayer script. It could break some other item's Use function because they would be expecting the script object rather than an entity object. I would change FPSPlayer script back to

if usableentity~=nil then

 --Use the object, whatever it may be
 usableentity.script:Use(self)

 

Then change the pickup script to

Script.battery_power = 50 --int "Battery power"

function Script:Use(person)
if person.battery_level ~= nil and person.battery_max_level ~=nil then
if person.battery_level < person.battery_max_level then
person.battery_level = person.battery_level + self.battery_power
if person.battery_level > person.battery_max_level then
person.battery_level = person.battery_max_level
end
self.entity:Release()
end
end
end

 

Allright that works as well, thanks hankinator!

 

 

 

I think some of these features would be best to be broken out into their own classes. Flashlight seems like a prime candidate for this. The FPS player script is already a big mess. Plus integration/distribution/understanding/modification will be much easier.

 

I think a great deal of things in the FPS player script could benefit from this and be much easier to manage and read.

 

I completely agree with you here, it's such a pain to change code in the FPSPlayer script sometimes, maybe it's time for an FPSPlayer script overhaul.

Link to comment
Share on other sites

In my experience this will result in a lot of brittle pieces that frequently stop working together. One object, one script, zero ambiguity.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

One object, one script, zero ambiguity.

 

Yeah, we've had this convo many times and don't agree smile.png

 

My time is short but would be interesting to see a clearly broken out fps script to see the difference in readability/maintainability/extensibility. I'm not talking component design to an insane level here, just classes that you manually include into the script to break out logical functionality. Movement & animation still makes sense for FPS script. Flashlight and all that goes with it seems to make sense to break out. Firebal could have taken the small and easy flightlight.lua file and adapted it easily enough to add his functionality I think. At least the task wouldn't seem so large and overwhelming as it does when he looks at the fps script today.

Link to comment
Share on other sites

Extensibility in most cases is really just indecisiveness. My $0.02.

 

Not in this specific case of a flashlight. You provided a basic flashlight. It wasn't indecisiveness on your part it was just providing basic functionality. You provided a basic template and he wants to extend it, but you didn't give a very good environment for extending it.

 

Even so though, indecisiveness is a human trait. We don't know everything. You've changed many things over time. We aren't perfect so making this process easier seems like a good idea. That's part of the idea. We aren't perfect and we may want to try many different things. This would also make people like the OP's job easier to provide such functionality if the environment promoted this. Right now he's following in the footsteps of your idea and that script just got bigger and messier (but it was kind of him to help for sure). If your template broke these things out, he'd be more inclined to follow that design pattern. This isn't all life or death stuff obviously, but it makes script writing better in my experience.

Link to comment
Share on other sites

Those big scripts can look complicated.

 

I have started doing more smaller scripts that can be used between projects. Adding to the script panel so properties dont need changing in code between projects.

Elite Cobra Squad

Link to comment
Share on other sites

Not in this specific case of a flashlight. You provided a basic flashlight. It wasn't indecisiveness on your part it was just providing basic functionality. You provided a basic template and he wants to extend it, but you didn't give a very good environment for extending it.

 

Even so though, indecisiveness is a human trait. We don't know everything. You've changed many things over time. We aren't perfect so making this process easier seems like a good idea. That's part of the idea. We aren't perfect and we may want to try many different things. This would also make people like the OP's job easier to provide such functionality if the environment promoted this. Right now he's following in the footsteps of your idea and that script just got bigger and messier (but it was kind of him to help for sure). If your template broke these things out, he'd be more inclined to follow that design pattern. This isn't all life or death stuff obviously, but it makes script writing better in my experience.

The purpose of this post was intended to show a basic concept. Most of the logic to achieve this battery would look pretty similar if it was developed utilizing a different design pattern. I expect that most people would take the concept and implement it in their scripts the way they see fit. This is part of the reason I didn't post the whole script, just the parts that matter. Besides, it's a moving target to satisfy everyone's code structure.

 

Do you have have a prototype of your ideal FPSPlayer script?

Link to comment
Share on other sites

I understand the purpose of this post and I don't have an issue with it and think it's great you provided your time to help but it highlighted the overall script design philosophy that Josh holds that I don't agree with (completely) and the fps script is a prime example as to why I don't agree with it and how we see users follow in that idea even if it's to just get the basic idea of something. If Josh took a more modular approach and broke that script out more you would have been less likely to go "inline" with your example and instead follow the pattern. The flashlight would have already had it's own script so you would have just modified that script and it would have been much easier to get the idea across and for Firebal to implement and potentially understand and play around with modifying it without worrying about screwing up the rest of the fps script. So really this was less of an issue with what you did and more with the "One object, one script, zero ambiguity" idea when scripts start getting rather large which is very common in all games.

 

Do you have have a prototype of your ideal FPSPlayer script?

 

I generally avoid that script for anything but a quick test, but I might take another look given this recent topic and split it out. Don't want to get too sidetracked from Dead Anyway (saying that in case tj is reading this :) ) but shouldn't take that long as an exercise.

 

The flashlight is an interesting and perfect example of all of this though. The idea of a flashlight can be pretty broad as we've seen in recent games. Cameras, cell phones, lamps, etc have acted as "flashlights" in games. Given a common interface for a "flashlight" it can really help people implement their own and separate out their logic to it's own script for easy distribution/maintenance/extensibility/etc. Really it comes down to what josk alludes to. Big scripts end up more complicated and fragile when modified than breaking out logical parts into multiple scripts. 1 script is great when you give it to someone who just accepts it and plugs it into the game, but this ends at the very basic beginner level in users. They soon want to start customizing their game and I feel it's easier and more friendly to do that (for everyone) with smaller logically broken out scripts that do their specific job without bleeding responsibility into other areas.

Link to comment
Share on other sites

Also if Josh updates the FPS script you will lose your modifications to it.

This is an annoying problem. When I get a LE update I cringe because it is awkward to go through the log and find all the .bak files to do a diff on the current script then merge the two scripts. I made sure to specify what version of LE I used when developing the tutorial. I've taken to making a copy of every script I modify (as I did in the this tutorial), updates be damned.

 

I like the idea of an improved FPSPlayer script(regardless of design pattern) but until it replaces the default LE scripts I think it is best to base a tutorial on the scripts that are known, working and available with the default installation.

Link to comment
Share on other sites

but until it replaces the default LE scripts I think it is best to base a tutorial on the scripts that are known, working and available with the default installation.

 

I agree. My posts weren't to try and make people do otherwise but to just show the benefits so maybe more people put more pressure on Josh to officially change the style with that script and maybe other bigger ones like it smile.png

 

Josh does listen to the community but if only 1 person is voicing his opinions it's harder to get any traction for change. I think the fps script is starting to get to a tipping point the more people want to add functionality around it.

 

721 lines. That's a lot of code in 1 file. I remember going through HL's code and was amazed at how small and modular it was in it's class design. Each function was 1 screen or less, often times less and from what I recall there weren't a ton of functions to a class. They separated things out nicely.

  • Upvote 5
Link to comment
Share on other sites

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.

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.

 Share

×
×
  • Create New...