I know this has been answered like 1000 times, but I just don't know how exactly should I code.
All I want is when the platform changes its position on the x or the z axis, then rotate the whole platform by 90 degrees.
I tried it with a platform.transform.Rotate(0, 90, 0), so I think there is more to do.
The code itself:
public GameObject platform;
public Transform lastPlatform;
Vector3 lastPosition;
Vector3 newPos;
bool stop;
private Quaternion rotationQuaternion;
void Start()
{
lastPosition = lastPlatform.position;
StartCoroutine(SpawnPlatforms());
rotationQuaternion = transform.rotation;
}
void Update()
{
}
IEnumerator SpawnPlatforms()
{
while (!stop)
{
GeneratePosition();
Instantiate(platform, newPos, rotationQuaternion * Quaternion.identity);
lastPosition = newPos;
yield return new WaitForSeconds(0.1f);
}
}
void GeneratePosition()
{
newPos = lastPosition;
int rand = Random.Range(0, 2);
if (rand > 0)
{
newPos.x += 1.5f;
transform.rotation = rotationQuaternion * Quaternion.Euler(0, 90, 0); //one way i tried
}
else
{
newPos.z += 1.5f;
platform.transform.Rotate(0, 90, 0) //another way I tried
}
}
I appreciate all the help!
So as I see it currently there are multiple issues / unclear things
you are rotating about 90° each 0.1 seconds, so within 1 second you rotate about 900°. That sounds to me like it is simply way to fast.
You also rotate transform and not the platform
platform seems to be a Prefab in the assets so it makes no sense to rotate it
You probably want to do
void GeneratePosition()
{
newPos = lastPosition;
int rand = Random.Range(0, 2);
if (rand > 0)
{
newPos.x += 1.5f;
}
else
{
newPos.z += 1.5f;
}
// rather directly rotate the quaternion parameter
// if you are going to do the same thing in both cases anyway I would rather extract it right away
rotationQuaternion *= Quaternion.Euler(0, 90, 0);
}
and then also
// Rotating by "Quaternion.idendity" has no effect
Instantiate(platform, newPos, rotationQuaternion);
Still that sounds like a lot of spawning to me, maybe you would want to look into Object Pooling
I managed to find a solution for the problem. Maybe could be helpful for someone, so here you go:
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public void Step(float distance)
{
if (Random.value < 0.5)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0);
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0);
}
//orientation = Quaternion.Euler(0, 90, 0) * orientation;
}
}
void Start()
{
_spawn.position = lastPlatform.position;
_spawn.orientation = transform.rotation;
}
From now on, all what's left to do is Instantiate:
var newPlatform = Instantiate(platform, _spawn.position, _spawn.orientation);
Huge thanks to derHugo for giving me a start on how to do that!
Related
I'm currently trying to use a Physics2D.BoxCast() to manually detect collisions in a test project. I got everything set up with the help of a tutorial I watched a while ago but for some reason the box cast seems to be above the Box Collider that I attached its size to. I'm not the best a writing issues out so Ill have a gif bellow that better shows what the problem is, as well as I'll attach the code I used to detect the collisions.
This is when I try to collide from +y on the axis
This is when I try to collide from -y on the axis:
I've tried looking up more on how Physics2D.BoxCast work and why it would seem to be pushed down but I havent found anything helpful just yet.
Here's the code I use for movement and manual collision detection.
using UnityEngine;
public class Player : MonoBehaviour {
public float speed = 2f;
private BoxCollider2D boxCollider;
private SpriteRenderer sprite;
private Vector3 moveDelta;
private RaycastHit2D hit2D;
protected virtual void Awake() {
boxCollider = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
}
protected virtual void Update() {
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
moveDelta = new Vector3(x, y, 0);
// Flips the sprite based on the direction its moving on the X
if (moveDelta.x > 0)
{
sprite.flipX = false;
}
if (moveDelta.x < 0)
{
sprite.flipX = true;
}
// Manual detection for collisions
hit2D = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, 0), Mathf.Abs(moveDelta.x * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(moveDelta.x * Time.deltaTime, 0, 0);
}
hit2D = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(0, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(0, moveDelta.y * Time.deltaTime, 0);
}
}
}
The transform.position used in the box cast was not at the center of the collider. Thus casting the BoxCast above where the collider was. The solution was to place an empty game object in the center of the collider. Again I'm sorry about the way I explain things I hope the pictures bellow can help you understand better..
Code wise it was a simple fix that took me too long to figure out..
using UnityEngine;
public class Player : MonoBehaviour {
public float speed = 2f;
public GameObject boxcastCenter;
private BoxCollider2D boxCollider;
private SpriteRenderer sprite;
private Vector3 moveDelta;
private RaycastHit2D hit2D;
protected virtual void Awake() {
boxCollider = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
}
protected virtual void Update() {
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
moveDelta = new Vector3(x, y, 0);
// Flips the sprite based on the direction its moving on the X
if (moveDelta.x > 0)
{
sprite.flipX = false;
}
if (moveDelta.x < 0)
{
sprite.flipX = true;
}
// Manual detection for collisions changes were made in the transform.position bellow
hit2D = Physics2D.BoxCast(boxcastCenter.transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, 0), Mathf.Abs(moveDelta.x * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(moveDelta.x * Time.deltaTime, 0, 0);
}
hit2D = Physics2D.BoxCast(boxcastCenter.transform.position, boxCollider.size, 0, new Vector2(0, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(0, moveDelta.y * Time.deltaTime, 0);
}
}
}
I made a speedometer script for rotaing the needle in the speedometer
public class Speedometer : MonoBehaviour
{
public float zeroRate = 3f;
[SerializeField] PlayerController vehicle;
void Update()
{
if (vehicle.IsOnGround())
{
float zRot = ((vehicle.speed * 2.237f) / vehicle.maxSpeed) * 180;
transform.rotation = Quaternion.Euler(0, 0, -zRot);
}
else
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.identity, zeroRate * Time.deltaTime);
}
}
}
I'm facing the problem that the needle sometimes does a full 360 to rotate towards Quanternion.identity. I could use an if condition to check if the value goes beyond 180, subtract the twice the difference but the condition would check for this in every Update() call and would just clutter code with a 1-time-use-only if-condition. Is there a function that restricts the rotate in clockwise/anti-clockwise direction?
Alternatively you could probably do something like store the last zRot value and over time reduce it and go
private float zRot;
void Update()
{
if (vehicle.IsOnGround())
{
zRot = ((vehicle.speed * 2.237f) / vehicle.maxSpeed) * 180;
transform.rotation = Quaternion.Euler(0, 0, -zRot);
}
else
{
if(zRot > 0)
{
zRot -= zeroRate * Time.deltaTime;
// clamp to 0
zRot = Mathf.Max(0, zRot);
transform.rotation = Quaternion.Euler(0, 0, -zRot);
}
}
}
Just that now zeroRate would be in °/second instead of radians/second in your version.
This camera script is intended to rotate and look at the player while it is moving and snapping to the player slowly while it isn't. (Player passes over a Vector3 beforeMoving before it is starting movement). My issue is I want to "feed" the deltaPosition slowly so it isn't a sudden snap but rather a slow and smooth transition, also stop adding if arrived.
private void LateUpdate()
{
if (player.isMoving)
{
desiredPosition = player.beforeMoving + offset;
}
else
{
Vector3 deltaPosition = player.transform.position - player.beforeMoving;
desiredPosition += deltaPosition * Time.deltaTime;
}
Quaternion camTurnAngle =
Quaternion.AngleAxis(input * rotationSpeed, Vector3.up);
desiredPosition = camTurnAngle * desiredPosition;
transform.position = Vector3.Slerp(transform.position, desiredPosition, smoothFactor);
transform.LookAt(player.transform);
}
Edit: I thought I would share the final code.
private void LateUpdate()
{
Quaternion rotation = Quaternion.Euler(GetDegree(), input.x * rotationSpeed, 0f);
if (player.isMoving)
{
desiredPosition = player.beforeMoving + offset;
CalculatePanTime();
}
else if (!player.isMoving)
{
desiredPosition = player.transform.position + offset;
}
transform.position = Vector3.Slerp(transform.position, rotation * desiredPosition, GetSpeed());
transform.LookAt(player.transform);
}
private void CalculatePanTime()
{
stoppedTime = Time.time;
playerDelta = Vector3.Distance(player.transform.position, player.beforeMoving);
timeToPan = (playerDelta / snappingSpeed) * Time.deltaTime;
}
private float GetSpeed()
{
if (Time.time < stoppedTime + timeToPan)
{
controlsDisabled = true; return snappingSpeed;
}
else
{
controlsDisabled = false; return smoothSpeed;
}
}
You are telling us what you want the code to do, that is good. You are also posting the code you implemented to achieve your goal, that is also good. Can you also tell us what is not working as you want it to work as a result of that code?
From what I understand it is the "Vector3 deltaPosition = player.transform.position - player.beforeMoving;
desiredPosition += deltaPosition * Time.deltaTime; " that is not behaving as you expect it to
maybe try something like this:
private void LateUpdate()
{
// snap the rotation center slowly to the player's position if not moving, or player's position before he started moving
desiredPosition = Vector3.Lerp(desiredPosition, player.beforeMoving, 0.1f);
// rotate around rotation center
Quaternion camTurnAngle = Quaternion.AngleAxis(rotationSpeed * Time.time, Vector3.up);
desiredPosition += camTurnAngle * offset;
// set the position to rotate around rotation center, and look towards player
transform.position = Vector3.Lerp(transform.position, desiredPosition, smoothFactor);
transform.LookAt(player.transform);
}
the problem with your code is that you are overshooting. If you want to implement something that decides how fast the camera snaps, or how much time it should take to snap, try introducing a float player.timeStopMoving = Time.time that you can use to correctly compute the position correction while he is not moving.
if(player.isMoving)
{
desiredPosition = player.beforeMoving;
}
else
{
const float timeNeededToSnap = 2f;
// or float timeNeededToSnap = (player.transform.position - player.beforeMoving).magnitude; // (which you could compute only once in player script, when he stops moving, and then reuse the value instead of performing a ".magnitude" every frame)
if(Time.time < player.timeStopMoving + timeNeededToSnap)
{
desiredPosition = Vector3.Lerp(desiredPosition, player.transform.position, (Time.time - player.timeStopMoving) / timeNeededToSnap);
}
else
{
// an other problem here is: if the player starts moving AGAIN before your desiredPosition got the value, do you want desired position to glich to the new player.beforeMoving?...
desiredPosition = player.transform.position;
}
}
EDIT:
To make he lerp less linear, you can use:
if(Time.time < player.timeStopMoving + timeNeededToSnap)
{
var t = Mathf.Cos(Maths.Pi * 0.5f * (Time.time - player.timeStopMoving) / timeNeededToSnap); // as timeDelta/totalTime goes from 0->1, multiply it by Pi/2 and the Cos will also go from 0->1 but with a smoothing speed
desiredPosition = Vector3.Lerp(desiredPosition, player.transform.position, t);
}
void Fire(float firingRate)
{
TimePerFrame += Time.deltaTime;
if(TimePerFrame >= firingRate)
{
Vector3 ProjectileDistance = new Vector3(0, 30, 0); //distance between center of the campion and it's head
GameObject beam = Instantiate(projectile, transform.position + ProjectileDistance, Quaternion.identity) as GameObject;
beam.GetComponent<Rigidbody2D>().velocity = new Vector3(0, projectileSpeed, 0);
// AudioSource.PlayClipAtPoint(fireSound, transform.position);
TimePerFrame = 0;
}
}
void Update ()
{
if (freezePosition == false)
{
Fire(firingRate);
PositionChaning();
firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, 0.1f);
Debug.Log(firingRate);
}
}
I want my firerate to be flexible, i want it to start by shooting fast and let it automatically lower it's fire rate. (the bigger the firingRate float is the slower the speed is)
The problem is that firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, 0.1f);
triggers once and only once. It doesn't seem to change it's value every frame.
Debug.Log(firingRate); tells the value every frame but it seems to remain a constant.
Why does this happen?
The update triggers every Frame, and so does your Mathf.Lerp However you are not changing the interpolation, which in your case is defined as 0.1f.
By changing this interpolation, you will be able to achieve the 'shifting' of fire rate.
In your case you could define a variable t outside the scope of your update, and update it inside the Update() through t += 0.5f * Time.deltaTime;
The Mathf.Lerp documentation has a pretty good sample of how todo so as well
void Update()
{
// animate the position of the game object...
transform.position = new Vector3(Mathf.Lerp(minimum, maximum, t), 0, 0);
// .. and increate the t interpolater
t += 0.5f * Time.deltaTime;
// now check if the interpolator has reached 1.0
// and swap maximum and minimum so game object moves
// in the opposite direction.
if (t > 1.0f)
{
float temp = maximum;
maximum = minimum;
minimum = temp;
t = 0.0f;
}
}
Your problem is here:
firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, 0.1f);
As you can see here your t has to be calculated every frame.
public static float Lerp(float a, float b, float t);
You can change it like this:
private float fireTimer = 1.0f;
public float fireLimiter = 0.05f;
void Fire(float firingRate)
{
TimePerFrame += Time.deltaTime;
if(TimePerFrame >= firingRate)
{
Vector3 ProjectileDistance = new Vector3(0, 30, 0); //distance between center of the campion and it's head
GameObject beam = Instantiate(projectile, transform.position + ProjectileDistance, Quaternion.identity) as GameObject;
beam.GetComponent<Rigidbody2D>().velocity = new Vector3(0, projectileSpeed, 0);
// AudioSource.PlayClipAtPoint(fireSound, transform.position);
TimePerFrame = 0;
}
}
void Update ()
{
if (freezePosition == false)
{
if(fireTimer > 0.0f){
fireTimer -= Time.deltaTime * fireLimiter;
}
Fire(firingRate);
PositionChaning();
firingRate = Mathf.Lerp(minFiringRate, maxFiringRate, fireTimer);
Debug.Log(firingRate);
}
}
Do not forget to reset fireTimer to 1.0f after shooting!
The delay of the firerate can be controlled by fireLimiter
I am trying to "squeeze" a gameObject. When distance starts to be 6.98, execute this code: "The smaller the distance between gameObject1 and gameObject2, the thinner and longer this gameObject is". Somehow, this code has no effect. Why?
public Transform gameObject1;
public Transform gameObject2;
void Update ()
{
float distance_squeeze = Vector3.Distance(gameObject1.position, gameObject2.position);
for (distance_squeeze = 6.98f; distance_squeeze > 0; distance_squeeze -= 0.1f)
{
transform.localScale += new Vector3(-0.5F, 0.5F, 0);
}
}
You are calculating the distance between gameObject1 and gameObject2 and then setting it equal to 6.98.
Try something like:
void Update ()
{
float distance_squeeze = Vector3.Distance(gameObject1.position, gameObject2.position);
if(distance_squeeze < 6.98F){
transform.localScale = new Vector3(-distance_squeeze, distance_squeeze, 1);
}
}