So, I’m pretty new to scripting, and I’m trying to create an object that causes gradual damage to the player over time. I had managed to get the script to cause damage over time, but it was way too fast, so I am trying to take a different route.
Right now, on my PlayerStatus script, I have this:
PlayerStatus.js
var health : int;
var points : int;
function Update () {
}
function ApplyTimedDamage (totalDamage : int) {
health -= totalDamage*Time.deltaTime*1;
}
function ApplyDamage (totalDamage : int) {
health -= totalDamage;
if (health <= 0) {
Kill ();
}
}
And on the object that causes damage is this:
IceDamage.js
var damage : int;
function Update () {
}
function OnTriggerStay(collisionInfo : Collider) {
var playerStatus : PlayerStatus = collider.GetComponent(PlayerStatus);
if(collisionInfo.gameObject.tag == "Ice"){
playerStatus.ApplyTimedDamage;
}
}
There’s more code that’s not really necessary, but I can post it if it’ll help.
If anyone can help, it would also be really great if you explained what you were doing and what I’m doing wrong, just so I can understand how it works. (Feel free to not explain, I just would really like it.)
If the player is walking in to the ice, why would the players tag be ice? Wouldn’t that be on the ice? (you are better off using layers anyway).
I believe a better pattern would be to pick a side to do all the work on. You could either have the ice reduce the life of the player directly, or let the player know they have been hit and let the player decide how to handle that. I think it comes down to how many variables will be involved, etc. For example, can a player walk in to two ice damage areas and get double the damage, or would ice make the player colde in which case the player would lose damage over time. In other words, can a player get more cold, or is cold a boolean. Also, is the amount of damage taken when cold going to be affected by a modifier, such as warm clothing? I would sketch all this out and think about where the control should be. Is the ice intelligent, or is the player, when it comes to figuring this stuff out?
Oh, and the short answer is, two things: One, do you have a global “tick” where damage is only applied every X seconds? …and two, you could put a variable called “dmgMult” and then multiply the equation with this. As you turn it up or down it would do more or less damage. There are a lot of ways to go about this.
This is going to be a VERY simple game (it’s an assignment for a class and the first game we’ve gotten to), so there’s not going to be any clothing, intelligence on the ice itself or any of that stuff involved.
Mostly, I have no idea how to go about creating something, and mostly I just want it to be as simple as possible.
What I want to happen is the player walks into the ice, and as long as they’re in there they will lose damage over time. So to answer one of your questions, cold would be a boolean.
I originally had the control on the ice, and that makes the most sense to me.
I don’t have any global tick that would apply damage, and I don’t know exactly how to go about doing that. Like I said, I am very much a beginner.
OK. well to keep it as simple as possible, you are already using a function which runs every frame, so just reduce the damage and your player will lose life more slowly.
You need to pass the damage from the ice though. and do the tag check first:
(Untested Code…)
function OnTriggerStay(other : Collider)
{
if(other.gameObject.tag != "Ice") return; // early out
var playerStatus : PlayerStatus;
playerStatus = collider.GetComponent.<PlayerStatus>(); // Generics are cool
if (playerStatus != null)
{
playerStatus.ApplyDamage(damage); // pass the damage.
}
}
Okay, so I’m not getting errors anymore, but I’m also not taking damage when entering the trigger.
PlayerStatus.js
var health : float;
var points : int;
function Update () {
}
function ApplyTimedDamage (totalDamage : int) {
health -= totalDamage*Time.deltaTime*1;
}
function ApplyDamage (totalDamage : int) {
health -= totalDamage;
if (health <= 0) {
Kill ();
}
}
function Kill () {
print("YOU ARE DEAD");
}
function AddPoints (totalPoints : int) {
points += totalPoints;
if (points >= 100) {
Continue ();
}
}
function AddHealth (totalHealth : int) {
health += totalHealth;
}
IceDamage.js
var damage : int;
function Update () {
}
function OnTriggerStay(other : Collider)
{
if(other.gameObject.tag != "Ice") return;
var playerStatus = GetComponent(PlayerStatus);
if (playerStatus != null)
{ playerStatus.ApplyTimedDamage(damage);
}
}
So this is what I have now? I’m pretty lost, honestly. I only understand about half of what I’m doing. Some of the extra things are just placeholders for stuff I haven’t finished, either.
Thank you guys for taking the time that you already have to help me. I really appreciate it.
The best way to debug code when you have no idea what is happening is with Debug.Log(0 messages. For example, are you sure ApplyTimedDamage is even running?
Sometimes I’ll just scatter around some log messages to see where the flow is. e.g.:
function OnTriggerStay(other : Collider)
{
if(other.gameObject.tag != "Ice") return;
Debug.Log(other.name + " is tagged 'Ice'");
var playerStatus = GetComponent(PlayerStatus);
if (playerStatus != null)
{
Debug.Log("playerStatus found, running ApplyTimedDamate(). Damage = " + damage);
playerStatus.ApplyTimedDamage(damage);
}
}
How do you know you aren’t taking damage? Are you looking at the inspector as the game runs?
Why are you using ApplyTimedDamage() at all? It isn’t checking for a death event. Why not use ApplyDamage() directly?
Also, you are using GetComponent on the IceDamage gameobject whereas I think you mean to get the component from the player:
var playerStatus = other.GetComponent(PlayerStatus);
Finally, you never answered one of my original questions. Why would you tag your player “Ice”? Why not “Player”. Are you sure your checking the right tag here?
I have a GUI to display health and points, so through that I can tell that it’s not affecting the damage. Even when I reference ApplyDamage() instead of ApplyTimedDamage() it doesn’t seem to work. Honestly, I don’t know why I have two different damage codes. I think I was thinking I’d need a different one for it to continuously cause damage, but it’s not really necessary.
I know the GUI works, since I have other codes that cause damage and they work fine in the GUI.
And the code that you have there, would that replace the code in the IceDamage.js? This one:
var playerStatus = GetComponent(PlayerStatus);
The player is not tagged. I hardly even know how tags work, and I’m just starting to get into that. The ice is tagged “Ice”, so I’m trying to check to see if the player has collided with that. I got the idea to use tags from another thread that was dealing with a similar issue.
I’m sorry that I am so slow on the uptake! Learning this stuff is like a second language and I’m sort of just jumping head first into it.
I don’t think you are slow, I think you are missing the forest through the trees (stand back from the code and think about what is happening).
Here is the situation:
You have a player “Player”
You have something which causes damage due to cold. Lets call it “Ice”
When the player is inside Ice’s collider, you want to trigger damage per frame “Damage”
The player has a script, a.k.a. a component. You called it PlayerStatus, i would call it just “Player” so it is clear this handles the player stuff
The ice has a script, a.k.a. component. You called it “IceDamage”, I would call it just “Ice” - keep it simple and generic until detail is called for.
Now, the implementation:
When Player is inside Ice’s collider, you want Ice to tell the Player that it needs to subtract damage.
You need to detect when objects enter Ice’s trigger, and if one of them is Player, then you execute. You are using a tag for this test (I’d use layers, but a tag will work). So, make sure the player has the tag, call it “Player”, so we can test for it. If an object with the tag “Player” is found, then you know you can do the heavy work of grabbing a component and processing the result. You wouldn’t want to attempt this on everything Ice comes in contact with since it would be a waste of time.
Finally, you should check if the object with the “Player” tag has the component Player, because it is bad form to assume such things in programming.
Now with this in mind, lets look at your question:
Does this: var playerStatus = GetComponent(PlayerStatus); replace this: var playerStatus = other.GetComponent(PlayerStatus); Yes. This is because the first version is calling get component on the Ice GameObject whereas you want to get the Player’s component from the passed collider reference. Make sense?
Some general stuff that may or may not help…
Three things I always told my students when I teaching scripting:
Use lots of log messages to see what you are doing
Never add anything you don’t understand (at least the theory if not the code), and use #1 to figure it out if you need to.
Always add new code one chunk at a time and never add the next chunk without testing and using for errors and #1. A chunk can be a single line of code until you get more familiar with things. This will keep you from creating compont issues that are 100x harder to figure out. When in doubt, divide-and-conquer… Comment everything out, then un-comment code one line at a time, using #1 above, until you find the source.
…and of course, write it all out in natural-language before writting out in code. Sometimes just writing functions and documentation without any code inside can be nice. There are a lot of tricks and everyone works differently, but when you start out you really have to try working like everyone else first.
Another simple solutiion would be to just send message on the colloder ( player in your case )
Set your collision matrix so the ice trigger care only player, and then in the OnTriggerEnter, do a SendMessage to fire either a script that will apply damage or coroutine, then check with on trigger exit to stop the decrase.
Some will said sendmessage is a big no no but as long you dont need to fire this everyframe that wont be a big deal believe me.
I don’t know what you mean by Matrix but this is a good solution. I didn’t recommend it because coroutines are a little more advanced and this is about basic flow.
There is nothing wrong with SendMessage() if it is the right tool for the job. For example, if this ice should just tell anything in its collider that it should be getting hurt, that makes the whole thing a lot more generic and SendMessage(“ApplyDamage”, damage) with the flag to make it optional is a great way to do this. In the case of the OP, only the Player is involved, so the original idea is the fastest performance wise and the most direct logically.
If you DID use SendMessage there would be less reason to filter using tags. Layers could still be nice though.
Yea theres a lot of people who undervalue SendMessage. Though with this example it is being ran every frame or more. But you’d want to use sendmessage if you were say having a seperate script that did things like blood splatter, GUI, health, ect. It makes your code base much more maluable, which is good.
With that said though one thing that hasnt been mentioned is OnTriggerStay is iffy, in that its not called every frame but every fixed update physics frame. So that could be multiple times per frame or every other frame depending on how long it takes to run a frame and the physics update rate.
Good point but that could actually be better since this is about damage over time from the player’s perspective. Basically, if the game lags the damage/second won’t (if I have this right).
I would use the OnTriggerEnter and Exit events to start a coroutine as well, but this is meant to be a simple example to understand the component flow and GameObject proximity detection.
Well in Op case , if it was me i will have an apply damage script on player where are teh collider too, and enable this script usingbsend message on trigger enter, as this get fired only once when enter, you can access disabled script through send message which is cool, when you do some component switch that get an update fct.
But yeah , thisis just another way, at least you can make you a generic trigger event sending using this , just have a string field for the message you want send.
Rafes, matrix collison layer let you define through your layer what object can interact which other with physics, this is pretty nice to control what layer should accept trigger or collision between them , and eliminate the need to check with what your collide with, in some case
This in under physics setting , matrix collisions layer
Thanks for all the information, you guys! I’ll have to sift through my code and fix things up some before I can tell you what the result is, though. I’ll probably have more questions soon, though.
Yep, Time.deltaTime whenever your in fixed update is the fixed delta. As long as your games running fast enough to avoid the max physics update rate it should be fine. But again its better to have your frame rate faster than the physics rate, while it sucks having physics some times lag behind (ie, render a couple of frames where theres no physics update ), it sucks more to have slow executing code on the physics and bring the framerate down contunally pulling performance to try to catch up.
But yea its good to understand the basics before diving into all of that.
var damage : int;
function Update () {
}
function OnTriggerStay(other : Collider) {
if(other.gameObject.tag != "Player") return;
var playerStatus = other.GetComponent(PlayerStatus);
if (playerStatus != null) {
playerStatus.ApplyDamage(damage);
}
Debug.Log(damage);
}
PlayerStatus.js
var health : float;
var points : int;
function Update () {
}
function ApplyDamage (totalDamage : int) {
health -= totalDamage;
if (health <= 0) {
Kill ();
}
}
Now I just need to know where and how I’m supposed to put in Time.deltaTime so the damage doesn’t happen every frame. I don’t want to mess things up too badly, so I need some assistance.