I currently learn unity3d and start to create an endless game, and I want to add score by a distance that player has traveled on + x axis, so I want to calculate only how far the player traveled on + x axis.. how to calculate it?
this is my scripts
public class Score : MonoBehaviour
public float distance;
public Transform player;
private float score = 0f;
public Text scoreText;
void Awake()
{
distance = Vector3.Distance(player.position, transform.position);
}
void Update()
{
if (distance > player.transform.position.x)
{
score = distance ++;
scoreText.text = distance.ToString();
}
}
}
You can have something like this to get you started. I set it to 0.1 distance to get +1 score but that number can be anything of course.
private const float DISTANCE_REQUIRED_FOR_1_POINT = 0.1f;
private Vector3 previousPlayerPosition;
private float distanceSinceLastScore;
public Transform player;
private float score = 0f;
public Text scoreText;
void Awake()
{
//At the start save the start position.
previousPlayerPosition = player.position;
}
void Update()
{
//Calculate the distance travvelled since last update.
distanceSinceLastScore += player.position.x - previousPlayerPosition.x;
//Set the previous position the current.
previousPlayerPosition = player.position;
int scoreToAdd = 0;
while (distanceSinceLastScore > DISTANCE_REQUIRED_FOR_1_POINT)
{
//Calculate how many points to add in this update. (Likely always 1 or none)
++scoreToAdd;
distanceSinceLastScore -= DISTANCE_REQUIRED_FOR_1_POINT;
}
if (scoreToAdd > 0)
{
score += scoreToAdd;//Add to the total score.
scoreText.text = score.ToString();//refresh the text display.
}
}
To be honest, you gave very little information about the game. But I will try to come up with some possible scenarios and solutions.
First of all, you stated that this is an endless game, then I don't know what distance are you calculating between the player and a point. So, your code didn't make much sense to me, please explain if I'm missing something.
Now, you said you want to calculate the score based on the distance that the player has traveled on + x-axis. So, take a look at this code:
float previousPlayerX;
public Transform player;
private float score;
void Awake()
{
previousPlayerX= player.position.x;
score = 0;
}
void Update()
{
if (previousPlayerX < player.position.x)
{
score += player.position.x - previousPlayerX;
previousPlayerX = player.transform.position.x
}
}
This code will increase the score as long as the player is running to +x. If the player stops or even turns back, the score will not be updated. Also, note that if your starting position is 0, then you don't need to subtract the two positions to add the score, you can simply go ahead and say score = player.position.x and it should give you the same result. Furthermore, I would implement this code in a coroutine with a little bit of wait time to avoid possible bugs maybe.
Now, let's think about another scenario. I don't think the character doesn't go to infinity in endless run games, generally, the character object is stable and you kinda make that illusion of endless flow. So, let's say your character's position is not changing actually, but in the game, it seems like your character is running non-stop. In such a case, you may want to increase the score based on the time passing.
private float score;
void Awake()
{
score = 0;
StartCoroutine(UpdateScore());
}
IEnumerator UpdateScore()
{
while(true)
{
score += 1;
yield return new WaitForSeconds(1);
}
}
As you can see in the second code, I'm just assuming that my character runs non-stop and I'm just increasing the score by 1 every second. You can do it in every half a second, you can increase the score by multiplying with a speed value or you can put an if statement and don't increase the score if the character is stunned for example. So, it's all up to you.
I hope that answer helps and gives you a general idea. Let me know in the comments if you have any other questions related to this.
Related
I'm making a small game in Unity and part of it it's that glass balls spawn every few seconds and they follow a path composed of targets, and the prefab has these targets specified:
Prefab picture
And here is the code:
public class move_to_target : MonoBehaviour
{
public GameObject[] target;
public float speed;
int current = 0;
float radius_target = 1;
// Update is called once per frame
void Update()
{
if (Vector3.Distance(target[current].transform.position, transform.position) < radius_target)
{
current = Random.Range(0, target.Length);
if (current >= target.Length)
{
current = 0;
}
}
transform.position = Vector3.MoveTowards(transform.position, target[current].transform.position, Time.deltaTime * speed);
}
}
So, whenever the ball spawns, it should go for the 1st target, then 2nd and finally the 3rd, but when I load the game, all the balls go to the horizon without stopping.
The problem seems to solve itself when I put the ball/fairy prefab in the scene and load the targets in the scene instead of the prefabs but that isn't the ideal solution.
How can I make it so the balls go to the targets in the scene?
Don't have enough rep to comment but this looks like it should work, what do you see if you Debug.Log(target[current].transform.position)?
If you don't need the target game object references, you could store Vector3[]'s instead.
A couple of things: You're randomizing your target every single update frame, so there is actually no progression from one target to the next in any kind of order. Is that intentional?
Also, you're moving the ball transform only when its distance is less than (<) a certain value. It seems more likely that you should be moving the ball as long as its distance is greater than (>) that value. Are you trying to go from a distance of greater than 1, to a distance of less than / equal to one?
If that's the case, maybe something like (untested):
public List<Transform> target = new List<Transform>();
public float speed;
int current = 0;
float radius_target = 1;
void Update()
{
if (Vector3.Distance(target[current].transform.position, transform.position) > radius_target)
{
transform.position = Vector3.MoveTowards(transform.position, target[current].transform.position, Time.deltaTime * speed);
} else {
current++;
if (current == target.Count)
{
current = 0;
}
}
}
But by this method the ball will seek to return to target[0] after target[2] (or whatever is the last one) and keep going forever in a loop. If you want to stop after reaching the last target you'd want to only execute this whole block if the current target < target.Count without returning to zero as it currently does.
And finally, your radius threshold of 1 could either work or not work, depending on how these targets are set up in the scene. This whole approach only works if they are all more than one unit (or radius_target units) away from each other.
I'm working on a vertical infinite runner, I'm attempting to grab the distance the player has moved in the y axis but only upwards, I don't want to the distance counter to count down, only up
Since it is a vertical game, jumping is used to get up to platforms, this means that I can't just base the distance counter on how long the player has stayed alive, since they could just stand there while the platforms just drag him down at a constant rate of -27f
I've attempted to just add -27 * Time.deltaTime to my count but that appeared to do absolutely nothing. Below is a counter that is working but only if my player is actually moving upwards, not stationary as I mentioned
public class Distance : MonoBehaviour {
public float distanceCount;
public Transform playerHeight;
public Text height;
public Text shadowHeight;
void Start () {
distanceCount = 0;
}
void Update()
{
distanceCount = (playerHeight.position.y + 0.09500122f) / 200f;
distanceCount = Mathf.Round(distanceCount * 10f) / 1f;
height.text = distanceCount.ToString() + " m";
shadowHeight.text = distanceCount.ToString() + " m";
}
}
If I could get some advice or an update to my current code that might solve for this I would really appreciate it, thank you.
I've been raking my brain for the last day on how to calculate a full rotation with how to count a full rotation of an object along the X axis thats using the base circular drive from SteamVR.
I thought a simple 3d cube, with the mesh turned off in the the path of the rotation with collision code on it would be a barebone way of doing it, but it doesn't even seem to be registering the detection when the object hits the placed cubes, and i know its not because of me being stupid, as its recycled code from a working part of the project.
Below i have a small piece of code that basically detects when the object has reached the end of the rotation, and then increments the Count by one.
My main problem is that sometimes it manages to clock more than once, and if you can find the right spot, you can just keep it there and it'll keep on adding the count up by. Im wondering how i can stop it and increment only by one, until another full rotation has been made?
EDIT: to be more clear in case there is any confusion, Once the angle is clocked in between 359 and 360, i want it to increment once, whereas currently if you get the angle to sit anywhere in between 359-360 it will carry on adding one to the rotation count, despite no full rotation having been made, so im trying to figure out how to make my code only increment once, and once it does increment once it resets the position to zero, so therefore no more Increments can happen. It's a crank mechanism in VR, along the X axis.
Any help is appreciated, Thanks!
float Test;
float RotationCount = 0;
// Start is called before the first frame update
void Start()
{
// Test = transform.localRotation.eulerAngles.x;
}
// Update is called once per frame
void Update()
{
if (Test > 359 && Test < 360)
{
Debug.Log("Clocked");
count();
}
else
{
// Debug.Log("Nope");
}
if (Test == 0)
{
Debug.Log("Yes");
}
Test = transform.localRotation.eulerAngles.x;
}
void count()
{
RotationCount++;
}
sometimes it manages to clock more than once, and if you can find the right spot, you can just keep it there and it'll keep on adding the count up by.
well in
if (Test > 359 && Test < 360)
{
Debug.Log("Clocked");
count();
}
what happens if your angle is e.g. 359.5?
It is very difficult to just take a current rottaion and know whether it was turned more or less than a certain angle.
I'ld rather store the last rotation, compare it to the current one and add the difference to a variable. Than if the variable exceeds 360 a full rotation was done. Since I also don't like to calculate anything with Quaternion and eulerAngles, way simplier is to use the methods provided by Vector3.
For the local rotation around X (== transform.right) I would use the angle between the current and the last transfrm.up vector.
Something like
public class RotationCheck : MonoBehaviour
{
public int RotationCount;
public float rotatedAroundX;
public Vector3 lastUp;
public UnityEvent On3TimesRotated;
private void Awake()
{
rotatedAroundX = 0;
// initialize
lastUp = transform.up;
}
private void Update()
{
var rotationDifference = Vector3.SignedAngle(transform.up, lastUp, transform.right);
rotatedAroundX += rotationDifference;
if (rotatedAroundX >= 360.0f)
{
Debug.Log("One positive rotation done", this);
RotationCount++;
rotatedAroundX -= 360.0f;
}
else if (rotatedAroundX <= -360.0f)
{
Debug.Log("One negative rotation done", this);
RotationCount--;
rotatedAroundX += 360.0f;
}
// update last rotation
lastUp = transform.up;
// check for fire the event
if (RotationCount >= 3)
{
On3TimesRotated?.Invoke();
RotationCount = 0;
}
}
}
You can use a UnityEvent to get the same thing the Button uses for onClick so you can reference callbacks there via the inspector.
BUT if you don't care about the single rotations but actually only wnat the final RotationCount >= 3 I would actually use
private void Update()
{
var rotationDifference = Vector3.SignedAngle(transform.up, lastUp, transform.right);
rotatedAroundX += rotationDifference;
RotationCount = Mathf.RoundToInt(rotatedAroundX / 360.0f);
// update last rotation
lastUp = transform.up;
// check for fire the event
if (RotationCount >= 3)
{
On3TimesRotated?.Invoke();
RotationCount = 0;
rotatedAroundX = 0;
}
}
which directly reduces the value by one if rotated under the 360 mark instead of waiting for a full negative rotation
*as you can see instead of reaching 3 the RotationCount is reset to 0. This is where the On3TimesRotated event is/would get fired.
Trying to create simple endless moving platform with 3 cubes of scale 70 on z(Player will not move forward, will just move left/right). The RepositionPlatform script is attached to each platform/cube which is responsible for movement and checks the z position of each platform and if it is <= -100.0f, then position is changed to (0,0,200.0f).
Problem is sometimes there is a little gap between the platforms(cubes) or there is a little overlap which I don't want.
Platforms should be placed one after each other without any gap or overlap!!!
Can anyone help find the issue looking at the script or suggest any other better way ?
The script below is attached to 3 platform game objects!!!
public class RepositionPlatform : MonoBehaviour
{
private GameObject platformGO;
[SerializeField]
private float speed;
// Start is called before the first frame update
void Start()
{
platformGO = this.gameObject;
Debug.Log("In RepositionPlatform Start method - "+ platformGO.name);
}
// Update is called once per frame
void Update()
{
Debug.Log("In RepositionPlatform Update Method- " + platformGO.name);
platformGO.transform.Translate(Vector3.back * Time.deltaTime * speed);
Transform platformTransform = platformGO.transform;
if(platformTransform.position.z <= -100.0f)
{
platformTransform.position = new Vector3(0,0,200.0f);
}
}
}
Probably because speed is a floating point value. You should read up on them if you haven't already.
Long story short, you aren't accounting for how far below -100 the value might have gone, you're just resetting it.
If you translate it instead, you will preserve any extra distance beyond -100 that the transform might have gone.
Try this instead:
If (transform.position.z < -100){
transform.Translate(new Vector3(0,0,200));
}
Edit
Should be Z value, not X
I am trying to make a game which "kind of" simulates the shooting of the "worms" game. The player can choose the position (circular) of an object and then, the force that is applied to the object should move in the direction its pointing towards. I tried using the AddForce(transform.right) code, but it would just go to the right. (2D BoxCollider and RigidBody2D)
Then comes the hard part, making the player choose the force by charging the power. When the player holds down the "f" key, I want the power to go up to a certain point. Once it reaches that point, I want it to go down again and then up again, so the player can choose the power he wants. I have no idea how to go about this.
It's been awhile since I've did Unity coding, so there may be some minor errors with my syntax but it should give you an idea of how to accomplish this. Your best bet for the loop is to use a coroutine to not block the main thread.
in Update() check for 'on key down' for F and start this coroutine:
IEnumerator Cycle()
{
float max = 10.0f;
float min = 1.0f;
float interval = 0.5f;
do
{
for(int i=min;i<max;i++)
{
PowerValue = i;
yield return new waitforseconds(interval);
if(!input.getkey(f))
break;
}
for(int i=max;i>min;i--)
{
PowerValue = i;
yield return new waitforseconds(interval);
if(!input.getkey(f))
break;
}
} while(input.getkey(f));
}
And back in update() use that powerValue with getKeyUp(f)
And here is PowerValue setup as a parameter that prevents code from setting the max and min outside of a 1 to 10 range (configurable)
private float powerValue = 1.0f;
public float PowerValue
{
get { return powerValue; }
set {
if(value>10f)
powerValue=10f;
else if (value<1f)
powerValue=1f;
else
powerValue=value;
}
}