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
Related
I'm new to Unity and C# in general, so excuse my terminology(or lack thereof)...
I have succeeded -with the help of a friend, to credit where its due- in making a platform go up a certain amount after one frame of collision with the player! The only problem is that, now, I can't seem to get it moving down again... Said friend challenged me to make it go up, stay airborne for a certain amount of time, then go back down.
Here is a snippet of my working code that makes it go up.
bool HaveCollided = false; //Collision checker boolean
void Update()
{
Vector3 up = lifter * Time.deltaTime;
if (HaveCollided != true || transform.position.y >= 5)
{
return;
}
}
void OnCollisionEnter(Collision collision) //Makes HaveCollided true at collision event
{
if (collision.gameObject.GetComponent<Playertag>() != null) //don't use GetComponent in Update()
{
HaveCollided = true;
}
So if my logic is right, I'd need to nest another if statement inside the one where the condition is: HaveCollided != true || transform.position.y >= 5 which should be:
if (newTime == Time.deltaTime * CertainAmount && transform.position.y >= 1) //make platform go down until y position is equal to 1
{
//make platform go down
Debug.Log("reached");
transform.position = transform.position - up;
}
But it's not working. It doesn't even seem to reach the part that would make the platform descend. I literally do not know where to go from here...
Based on your comment I made a few revisions to make the platform movement a bit smoother. I also made it so the same function can be used for both the upward and downward motion.
using UnityEngine;
using System.Collections;
[SerializeField] private float timeToMovePlatform = 0.0f; // time it takes the platform to get from 1 point to another
[SerializeField] private float timeToRestAtPeak = 0.0f; // time that the platform rests at the top of its movement
[SerializeField] private float platformYOffset = 0.0f; // offset the platform moves from its origin point to float upward
private Coroutine movingPlatform = null; // determines if the platform is currently moving or not
private void OnCollisionEnter(Collision col)
{
// add a tag to your player object as checking a tag at runtime vs. a GetComponent is faster
if(col.gameObject.tag == "Player")
{
// only trigger when we are not currently moving
if(movingPlatform == null)
{
// start our coroutine so the platform can move
movingPlatform = StartCoroutine(MovePlatform(true));
}
}
}
/// <summary>
/// Moves this object up or down depending on the parameter passed in
/// </summary>
/// <param name="moveUpward">Determines if this object is currently moving upward or not</param>
private IEnumerator MovePlatform(bool moveUpward)
{
// store our start position
Vector3 startPos = transform.position;
// build our goal position based on which direction we are moving
Vector3 goalPos = new Vector3(startPos.x, startPos + (moveUpward ? platformYOffset : -platformYOffset), startPos.z);
// set our current time
float currentTime = 0.0f;
while(currentTime <= timeToMovePlatform)
{
// lerp the position over the current time compared to our total duration
transform.position = Vector3.Lerp(startPos, goalPos, currentTime / timeToMovePlatform);
// update our timer
currentTime += Time.deltaTime;
// need to yield out of our coroutine so it does not get stuck here
yield return null;
}
// just in case there are any floating point issues, set our position directly
transform.position = goalPosition;
// when we are moving upward, make sure to stop at the peak for the set duration
if(moveUpwards)
{
yield return WaitForSeconds(timeToRestAtPeak);
// once we are done waiting, we need to move back downward
StartCoroutine(MovePlatform(false));
}
else
{
// we finished moving downward, so set our coroutine reference to false
// so the player can set the platform to move again
movingPlatform = null;
}
}
Fair warning, I did not test this code it is more a direction I would take for your situation. Before doing anything, add a tag to your player. If you are unsure what tags are, check out the docs. Be sure to give the player the tag Player so that the code will work as expected.
You will notice the first three variables are Serialized but are private. By doing this, it allows for Unity to show them in the inspector but not allow other scripts to touch them.
To learn more about Coroutines, check out the docs and for Lerps, I enjoy this blog post. Let me know if this works for you and/or you have any questions.
Your best bet is to add colliders and use isTrigger. Seed a time and if it's not triggered within that time period raise an event.
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 am working on a propably simple script. As I am new to coding and stuff, there is a high chance that my code looks horrible.
Okay, so here's the thing:
I have an enemy triggered, and only spawning when the player get's near a certain point. Then the Enemy has to follow the player, doesn't matter where he is, and keeping a certain range of 3 units.
To this point. Everything works fine.
Now, what doesn't seem to work is, that I need my enemy to "orbit" around my player, when he is in a certain range (3) and only then.
For now, it's orbiting right from the start...what did I miss??
Thats my code so far:
public Transform mTarget;
float mSpeed = 10.0f;
const float EPSILON = 3.0f;
public float speed;
void Start()
{
OrbitAround ();
}
void Update() {
transform.LookAt (mTarget.position);
if ((transform.position - mTarget.position).magnitude > EPSILON)
transform.Translate (0.0f, 0.0f, mSpeed * Time.deltaTime);
}
void OrbitAround() {
if(Vector3.Distance(transform.position, mTarget.transform.position) < 3) {
transform.RotateAround (mTarget.transform.position, Vector3.up, speed * Time.deltaTime);
}
}
Big Thanks in advance, if anyone can help me out.
Cheers,
As #Zibelas said, the Start function is only once.
The Unity documentation states:
Start is called on the frame when a script is enabled just before any of the Update methods is called the first time.
So try putting the call to OrbitAround() in the Update function and it should work
Well, didn't really fixed this problem exactly, but I found a way to work around it.
I just enabled the script to orbit around the target, when the enemy get close to it and disabled it, when the player goes away again.
void Update (){
if (Vector3.Distance (transform.position, mTarget.transform.position) < 15) {
script.enabled = true;
}
if(Vector2.Distance(transform.position, mTarget.transform.position) >15) {
script.enabled = false;
}
Still, thanks for the comments and information, guys.
I am a new student working on a class project. I have 1 script attached to the camera in my only scene. I want the camera to pause over the 1st object, scroll to the 2nd object and pause then scroll to the 3rd object and pause then end. Putting this code in the UPDATE, the camera never stops. Here in the START, it hesitates around 15 sec and then it goes right to the last object, then the function stops. Note the delay set for 10 seconds. I tried putting the code in a function and calling the function from START… but no good. What am I doing wrong? HELP ME OB1....
One more thing... Is START the best place to play sound?
using UnityEngine;
using System.Collections;
// I want the camera to pause over the 1st object, scroll to the 2nd object and pause
// then scroll to the 3rd object and pause then end. Putting this code in the UPDATE
// the camera never stops. Here in the START, it hesitates around 15 sec and then it
// goes right to the last object, then the function stops. Note the delay set for 10
// seconds.
public class CameraControl : MonoBehaviour
{
public float speed; // How fast to move the camera
public int moves; // How many moves to make
public float MyWait; // How long to pause over object
// Use this for initialization
void Start()
{
StartCoroutine(MyDelay());
for (int y = 1; y <= 2; y++) // go to the next two objects
{
for (int i = 1; i <= moves; i++) // Move the camera to the next position
{
Camera.main.transform.Translate(new Vector3(1.0f, 0.0f, 0.0f) * speed * Time.deltaTime);
Debug.LogFormat("moves = {0} ", i);
}
StartCoroutine(MyDelay());
}
}
IEnumerator MyDelay()
{
yield return new WaitForSeconds(10.0f);
}
}
Try placing this code on your camera and place all the game objects you'd like the camera to move to in the Objects list. If you'd like the camera to be a little further back so it can see the object, create a new Vector3 instead of simply giving the exact position and then give that new Vector3 the x, y and z of the iterating object and then add distance to which ever axis you'd like for the camera to be distanced from the object.
public float MyWait = 5; // How long to pause over object
public float speed = 5f; // How fast to move the camera
public List<GameObject> Objects; //List of each object for the camera to go to
void Start()
{
StartCoroutine(MoveToObject(0));
}
IEnumerator MoveToObject(int iteratingObject)
{
//Wait for however many seconds
yield return new WaitForSeconds(MyWait);
bool atDestination = false;
//Move the camera until at destination
while (!atDestination)
{
yield return new WaitForFixedUpdate();
transform.position = Vector3.MoveTowards(transform.position, Objects[iteratingObject].transform.position, Time.deltaTime * speed);
if (transform.position == Objects[iteratingObject].transform.position)
atDestination = true;
}
//Continue iterating until moved over all objects in list
if(iteratingObject != Objects.Count - 1)
StartCoroutine(MoveToObject(iteratingObject + 1));
}
I think you're going to need to put some code in the Update function for this to work smoothly.
Time.deltaTime will only really make sense in an Update function, using it here and trying to do everything in the Start function won't work. Also setting the Translate transform will instantly set the position to the given value. Look up linear interpolation (lerp).
I would suggest you have a member that you use to track the current state, i.e. which object you're looking at, but an Enum of states might be easier to read.
Then you can keep a member for how long you've been in that state, which you can increase in the Update.
Then within the Update you can check whether it is time to change state or update your moving camera.
Good luck!
I'm writing a 2D game and I'm trying to get moving platforms to work. After doing some previous investigation, I have it ALMOST working. The idea is to have 2 platform objects with colliders: 1 a visible object, the other an invisible object with isTrigger set (since the player would just go through a trigger). The code for the Moving Platform child (the trigger one) is set here.
using UnityEngine;
using System.Collections;
public class MovingPlatformChild : MonoBehaviour
{
public string parentPlatform = "";
void Start ()
{
transform.parent = GameObject.Find(parentPlatform).transform;
}
// Update is called once per frame
void Update ()
{
}
void OnTriggerEnter(Collider playerObject)
{
Debug.Log ("enter moving platform");
if(playerObject.gameObject.name.Contains("Player"))
{
playerObject.transform.parent = gameObject.transform;
}
}
int i = 0;
void OnTriggerStay(Collider playerObject)
{
Debug.Log ("stay" + i++);
if(playerObject.transform.position.y >= transform.position.y)
{
playerObject.transform.parent = gameObject.transform;
}
else
{
playerObject.transform.parent=null;
}
}
void OnTriggerExit(Collider playerObject)
{
Debug.Log ("EXIT");
if(playerObject.gameObject.name.Contains("Player"))
{
playerObject.transform.parent=null;
}
}
}
The Start() function just makes it a child of the visible platform. This can probably be done right in the Unity editor as well, instead of through code.
The OnTriggerEnter function adds the player object as a child of the trigger platform object, which is a child of the visible platform. So they should all move together.
The OnTriggerStay is an attempt to verify that this remains true only while the player is on the top of the platform. While the player is within the trigger, if the player is on top of the platform, then it remains attached. Otherwise, it's not. This is so that nothing happens on the bottom end.
The OnTriggerExit function just removes the player object as a child when it exits the trigger.
This is somewhat working (but we know somewhat isn't good enough). It works sometimes, but the player will be very jittery. Also, on the way down while standing atop the platform, the TriggerStay function doesn't appear to be called (implying the player is no longer within the trigger). This is observed through my Debug "stay" statement. Finally, sometimes the player will also fall straight through the platform.
What in this code would allow the player to fall through the platform, or be so jittery on the way up? Am I missing something crucial? If you need any more code, please let me know.
Below is the code for the movement of the non-trigger platform (the parent of the trigger platform and in an identical position). I will also share the Player's Update function after that.
void Start ()
{
origY = transform.position.y;
useSpeed = -directionSpeed;
}
// Update is called once per frame
void Update ()
{
if(origY - transform.position.y > distance)
{
useSpeed = directionSpeed; //flip direction
}
else if(origY - transform.position.y < -distance)
{
useSpeed = -directionSpeed; //flip direction
}
transform.Translate(0,useSpeed*Time.deltaTime,0);
}
And now the player code:
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
float rotation = Input.GetAxis("Horizontal");
if(controller.isGrounded)
{
moveDirection.Set(rotation, 0, 0); //moveDirection = new Vector3(rotation, 0, 0);
moveDirection = transform.TransformDirection(moveDirection);
//running code
if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held
{ running = true; }
else
{ running = false; }
moveDirection *= running ? runningSpeed : walkingSpeed; //set speed
//jump code
if(Input.GetButtonDown("Jump"))
{
//moveDirection.y = jumpHeight;
jump ();
}
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
EDIT: I've added the specifications for the platforms and player in this imgur album:
http://imgur.com/a/IxgyS
This largely depends on the height of your trigger box, but it's worth looking into. Within your TriggerStay, you've got an IF statement concerning the player y coordinates. If the trigger box is fairly large and the platform's speed fast enough, on the way up and between update ticks the player Y coords could momentarily be smaller than the trigger Y coords. This would lead to him losing the parentage, only to regain it a few ticks later. This might be the cause of the 'jittering'.
The problem I was having included
The moving platform was written using Translate. I rewrote it using a rigidbody and the rigidbody.Move function. This didn't immediately help, but...
I realized the CharacterMotor script (Unity provides this) that I had attached to the player included moving platform support. I set the MovementTransfer value to PermaLocked, and also unchecked the "Use FixedUpdate" box on the script, and it now works 99% of the time. I've had one time where I did a particular behaviour and slipped through, but I can't recreate it.
Hope this helps anyone who might be looking for an answer!