I'm facing a problem in my scene. What i'm doing is there are two panels both panel has one button which performing OnClick() listener with the method name RotateCamera().
Also in hierarchy there is a MainMenu gameobject which attached with one script. When I play the scene and click on panel one button than MainCamera rotate with 90 angle to the direction of second panel. Script works fine but I want to smoothly rotate camera to that panel when I click on that button - right now, it is rotating instantly. I don't know what I'm doing wrong on this script.
public class CameraSmooth : MonoBehaviour {
private bool check = false;
private Transform from;
private Transform to;
void Start(){
from = Camera.main.transform;
}
void Update(){
if (to != null) {
Camera.main.transform.rotation = Quaternion.Slerp (from.rotation, to.rotation, 3 * Time.deltaTime);
}
}
public void RotateCamera(){
if (!check) {
Camera.main.transform.Rotate (0,90f,0f);
to = Camera.main.transform;
check = true;
}
else if (check) {
Camera.main.transform.rotation = Quaternion.identity;
to = Camera.main.transform;
check = false;
}
}
}
The main problem here is that you're calling Camera.main.transform.Rotate() in RotateCamera(). This causes the rotation to be applied to the camera immediately, resulting in the instant rotation you're seeing.
Another small misconception - since Transform is a reference type, from and to always actually point to the same instance (the Transform of the camera)! Lastly, the value of 3 * Time.deltaTime will always average around 3/60, and will likely never end up close to 1, which is needed for an interpolation to be complete. As such, the following line:
Camera.main.transform.rotation = Quaternion.Slerp (from.rotation, to.rotation, 3 * Time.deltaTime);
will not be able to do anything even if the first issue is addressed.
The situation calls for a different solution, in my view:
Storing the target rotations in Quaternion variables rather than setting them directly on the Transform, and keeping track of the camera's initial rotation
Maintaining a timer which keeps track of the progress of the rotation (or you can abandon Slerp() and just apply an incremental rotation directly, that's also a valid approach)
Here's my suggested update to your code:
public class CameraSmooth : MonoBehaviour {
private bool check = false;
private float rotationProgress = 1;
private Quaternion initial;
private Quaternion from;
private Quaternion to;
void Start(){
// Cache the starting rotation, so we can calculate rotations relative to it
initial = Camera.main.transform.rotation;
}
void Update(){
if (rotationProgress < 1) {
rotationProgress += 3 * Time.deltaTime;
Camera.main.transform.rotation = Quaternion.Slerp (from, to, rotationProgress);
}
}
public void RotateCamera(){
from = Camera.main.transform.rotation;
rotationProgress = 0;
if (!check) {
to = initial * Quaternion.Euler(0,90f,0f);
check = true;
}
else if (check) {
to = initial;
check = false;
}
}
}
(Haven't tested this code, so please let me know if there are any issues.) Hope this helps! Let me know if you have any questions!
Related
enter image description here
enter image description here
I made this slingshot with objects in Unity3D. I know im suppose to add components but I dont know exactly which ones. The two yellows would represent as my elastics. Keep in note i have a robot arm made on the side. My claw hand will grab the object holder and pull it back so it can shoot. I need help in
what component should i add to them to make them like rubberband or elastic
how attach them to my objectHolder
how to make it shoot
please help and be patient with me :( Any advice I would appreciate it. Thank you :)
private Animation anim;
Rigidbody rb;
void Start()
{
anim = gameObject.GetComponent<Animation>();
}
void Update()
{
//********************Open pincher ********************
if (Input.GetKey(KeyCode.X))
{
anim.Play("clawopen");
//rb.AddTorque(Vector3.up * speed);
}
//*******************Close pincher ********************
if (Input.GetKey(KeyCode.Y))
{
anim.Play("clawclose");
// rb.AddTorque(Vector3.down * speed);
}
//*******************if not both ********************
else
{
rb.angularVelocity = Vector3.zero;
}
}
}
There is no magic component that will achieve what you want. You should break it down into smaller chunks.
Start with the movement of the object holder. There are different ways to achieve this. If you want to manipulate it via mouse, you can write a script that makes the objectHolder follow mouse movement while a mouse button is pressed. Once you release the mouse button you can let the object holder smoothly go back to it's start position.
For the elastic parts, I would replace the objects with LineRenderers. But also here are different ways to achieve this.
Maybe this tutorial helps you.
attach a spring component to the dark orange round shape and set connectedAnchor to the position of where it should stretch from. You can play around with soeing and damoer to make it more or less stretchy.
I would reccomend a very high spring (spring should be about 100-1000) and mass (mass should be about 5-10) on this. The ball should have a mass of 1 or less.
You can set the Rigidbody.position to the claw's position (while it is closed) Then when it opens' it will release and launch.
For example SlingShot.cs (on the dark orange object.
[HideInInspector] public bool closedClaw = false;
[HideInInspector] public Vector3 clawPos;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
if (closedClaw)
{
rb.positon = clawPos;
}
}
And then somewhere in the script that closes the claw
SlingShot ss;
GameObject slingShot;
void Start()
{
ss = slingShot.GetComponent<SlingShot>();
}
...
if (right claw.localEulerAngles.z /*or whatever axis it rotates on*/
> 90f /*or whatever threshHold*/)
{
ss.closedClaw = true;
}
else
{
ss.closedClaw = false;
}
ss.clawPos = rightClaw.transform.position +
leftClaw.transform.position;
ss.clawPos /= 2f;
All of this is untested and may have errors.
If this all works,I will come back later and do the stretchy yellow rubber bands.
Requested by comment Open closing arm
public float speed;
public Transform rightClaw;
public Transform leftClaw;
public Vector3 axis;
public Vector2 minMaxRotation;
void Update()
{
if (Input.GetKey(KeyCode.X))
{
Press(1);
}
if (Input.GetKey(KeyCode.Y))
{
Press(-1);
}
}
void Press(int open)
{
float r = rightClaw.transform.localEulerAngles * axis;
rightClaw.transform.localEulerAngles =
axis * Mathf.Clamp(r + speed * open, minMaxRotation.x, minMaxRotation.y);
float l = leftClaw.transform.localEulerAngles * axis;
leftClaw.transform.localEulerAngles =
axis * Mathf.Clamp(l - speed * open, 180f - minMaxRotation.x, 180f - minMaxRotation.y);
}
I have a character selection screen, when a certain button is pressed the assigned character will slide into view. Unfortunately for one of my character's they transform position seems to change putting them higher, etc making them not appear on the game screen. I'm not sure why as both character's have the same components and no where in code tells them to change position. The only script they have works as slide animation to make seem as though they glide into view instead of just appear. (attached below)
If I change the character's avatar int he animator to the same as the other character's then their position will be correct but then they will not do any animations.
Winter is the working GameObject.
Eunha is the one that changes position. Before the game starts Eunha's transform position is the same as Winter's shown in the Inspector.
private Vector3 startPosition;
private void Awake()
{
finalPosition = transform.position;
startPosition = finalPosition - transform.right * 5.0f;
}
private void Update()
{
transform.position = Vector3.Lerp(transform.position, finalPosition, 0.1f);
}
private void OnEnable()
{
transform.position = startPosition;
} ```
The codeing order seems to be problem. I recommend using 2 empty objects to determine location. This script is more efficient and reliable.
public Transform startPosition; // place on start position
public Transform endPosition; // place on shifted position
public float duration = 1f;
public void OnEnable() => StartCoroutine(Shift());
public IEnumerator Shift()
{
var shiftProgress = 0f;
while (shiftProgress < 1)
{
shiftProgress += Time.deltaTime/duration;
transform.position = Vector3.Lerp(startPosition.position, endPosition.position, shiftProgress);
yield return new WaitForEndOfFrame();
}
}
I am new to Unity so forgive me if I just did a stupid mistake. I am currently watching a tutorial from Brackeys, but I wanted to challenge myself by figuring out the movement myself. I got the moving forward correct, but I couldn't do the sideways movement. Since I've been spending a long time on the sideways movement, I just decided to watch the tutorial, but even when I used their code, it still didn't work. Can someone tell me why this isn't working? (FYI, in the code below, I did set all the public variables to some type of value).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sides : MonoBehaviour
{
public KeyCode left;
public KeyCode right;
public float sidewaysForce;
Rigidbody rb;
Vector3 v3;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetKey(left))
{
Debug.Log("Left");
rb.velocity = new Vector3(-sidewaysForce, 0, 0);
}
if (Input.GetKey(right))
{
Debug.Log("Right");
rb.velocity = new Vector3(sidewaysForce, 0, 0);
}
}
}
try left and right between quotation marks:
if (Input.GetKey("left"))
{
Debug.Log("Left");
rb.velocity = new Vector3(-sidewaysForce, 0, 0);
}
if (Input.GetKey("right"))
{
Debug.Log("Right");
rb.velocity = new Vector3(sidewaysForce, 0, 0);
}
You can check how to get the different keys in:
InputManager is in: Edit -> ProjectSettings -> Input
Other option is to use KeyCodes, which I prefer instead of the string argument for the intellisense benefits:
Update()
{
if ( Input.GetKey(KeyCode.UpArrow) )
//move up
if ( Input.GetKey(KeyCode.DownArrow) )
//move down
if ( Input.GetKey(KeyCode.RightArrow) )
//move right
if ( Input.GetKey(KeyCode.LeftArrow) )
//move left
}
If you're not getting the logs you added in the console, then you may have forgotten either:
To set the values of variables left and right in the inspector.
To add the Sides component to your GameObject.
I will assume your KeyCodes and the float are configured in the Inspector.
Either way you shouldn't do it like that!
You are completely overwriting the forward/backward movement and also the up/down movement with a hard 0 => you won't have any gravity and might break the forward movement - depending on which script is executed last.
You didn't share the forwards movement code so I can only guess but I suspect that there you do the very same thing like e.g.
rb.velocity = new Vector3(0, 0, someValue);
and therefore erase any sidewards movement from this script ;)
I would
a) make sure to have all movement input related stuff in a single script. Everything else just makes it unnecessarily hard to maintain and debug
b) make sure that your inputs don't eliminate each other
So something like e.g.
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Config")]
[SerializeField] private KeyCode _forward = KeyCode.W;
[SerializeField] private KeyCode _backward = keyCode.S;
[SerializeField] private KeyCode _left = KeyCode.A;
[SerializeField] private KeyCode _right = KeyCode.D;
public float speed;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpate()
{
// by default input is 0,0,0
var input = Vector3.zero;
// user input for forward
if(Input.GetKey(_forward)) input.z = 1;
else if(Input.GetKey(_backward)) input.z = -1;
// user input for sidewards
if(Input.GetKey(_left)) input.x = -1;
else if(Input.GetKey(_right)) input.x = 1;
// make sure both combined are not bigger then 1 (avoid faster diagonal movement)
input = Vector3.ClampMagnitude(input, 1);
// apply speed multiplier
var velocity = input * speed;
// preserve the Y velocity
velocity.y = _rigidbody.velocity.y;
// finally assign back to rigidbody
_rigidbody.velocity = velocity;
}
}
In general though way more flexible and without having to configure and check the KeyCodes manually you could rather use Input.GetAxis and do e.g.
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private Rigidbody _rigidbody;
[Header("Config")]
public float speed;
private void Awake()
{
if(!_rigidbody) _rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpate()
{
var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// make sure both combined are not bigger then 1 (avoid faster diagonal movement)
input = Vector3.ClampMagnitude(input, 1);
// apply speed multiplier
var velocity = input * speed;
// preserve the Y velocity for the gravity
velocity.y = _rigidbody.velocity.y;
// finally assign back to the rigidbody
_rigidbody.velocity = velocity;
}
}
You can configure the mappings in Edit → Project Settings → Input Manager but by default the
Horizontal already refers to both A/D as well as LeftArrow/RightArrow
Vertical already refers to both W/S as well as UpArrow/DownArrow
and additionally GetAxis slightly interpolates the input so it doesn't immediately jump from 0 to 1 and back but rather increases and decreases smoothly over some frames.
If the latter bothers you you can also use GetAxisRaw which does not do the smoothing.
I just realized that I did in fact made a stupid mistake. In my other script that makes the player move forward, it sets the velocity to (0, 0, 100) in the FixedUpdate() which meant that the x axis doesn't change. Sorry for wasting your time.
enter image description hereI am able to set Y axis of my player with a simple transform.position call, in a single step, all within onTriggerEnter method, but the motion has a single step and is therefore jerky. Now I am trying to make the motion smooth by putting the transform.position function in an Update method within the same class. However, it seems that the position values determined/updated by onTriggerEnter method are not accessible in the Update function. If I print the x and z values to console, they contain expected values from onTriggerEnter function, but appear to be 0 when I print to console from the update function.
Any ideas of what I am doing wrong?
I would never call myself a programmer, so assume the worst :-)
Thanks in advance for any help!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Step1SetElevation : MonoBehaviour
{
private float moveSpeed = 3f;
private float currX = 0.0f;
private float currZ = 0.0f;
private Vector3 currentPos;
private GameObject player;
private Collider other;
void OnTriggerEnter(Collider other)
{
player = GameObject.FindWithTag("Player");
currentPos = GameObject.Find("PlayerController").transform.position;
currX = currentPos.x;
currZ = currentPos.z;
}
void Update()
{
player = GameObject.FindWithTag("Player");
player.transform.position = new Vector3(currX, 3.4f, currZ) * Time.deltaTime * moveSpeed;
}
}
I believe your problem may be that you are using OnTriggerEnter() to set the position you want your player to move to, this will be called on the frame your player enters your trigger but then won't be called again until the player left and re-entered...
If you instead use OnTriggerStay() - something like
void Start(){ // this lookup can be expensive so lets only do it once
player = GameObject.FindWithTag("Player");
playerController = GameObject.Find("PlayerController");
}
void OnTriggerStay(Collider other){ // this is called once per frame that a collider remains in a trigger
if(other.gameObject.tag == "Player"){ // just in case anything else ever enters the collider
currentPos = playerController.transform.position;
currX = currentPos.x;
currZ = currentPos.z;
}
}
void FixedUpdate(){ // it's generally advised to move objects in fixed update
player.transform.position = new Vector3(currX, 3.4f, currZ) * Time.fixedDeltaTime * moveSpeed;
}
I have a spawner object. Every time a gameobject is spawned, I want that object to move randomly (wandering). The problem in my script is that the gameobject movement is very random (jittery). How can I solve this?
void Start ()
{
InvokeRepeating("SpawnNPC", Spawntime, Spawntime);
}
// Update is called once per frame
void Update () {
population = GameObject.FindGameObjectsWithTag("NPCobject");
for (int i = 0; i < population.Length; i++)
{
getNewPosition();
if (population[i].transform.position != pos)
{
population[i].transform.position = Vector3.MoveTowards(population[i].transform.position, pos, .1f);
}
}
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
I made the New randomize vector in different method, because I plan to change it with pathfinder function and make it in different thread/task.
You are choosing a new direction every single frame. That will always be very jittery. You wan't to only change direction after, at least, a small interval. Here is a link to one way to do that from Unity's website. https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
What about using Navigation? As you said wandering, I thought it would give you a nice result and also make your code simple.
The following screenshot is a sample with Navigation. The moving game objects are also changing their direction nicely, although it cannot be seen in the sample because the game object is capsule...
Ground game object in the sample program has NavMesh. See here to build NavMesh.
Agent game object has NavMeshAgent Component. See here to set it up.
Th Behaviour class below is for Agent game object.
using UnityEngine;
using UnityEngine.AI;
public class NavAgentBehaviour : MonoBehaviour {
public Transform[] Destinations { get; set; }
// Use this for initialization
void Start ()
{
InvokeRepeating("changeDestination", 0f, 3f);
}
void changeDestination()
{
var agent = GetComponent<NavMeshAgent>();
agent.destination = Destinations[Random.Range(0, Destinations.Length)].position;
}
}
The next Behaviour class is just for spawning the Agent and setting up the destinations. On Unity, set it to whatever game object in the scene, and allocate game objects to the fields.
using UnityEngine;
public class GameBehaviour : MonoBehaviour {
public GameObject Agent;
public Transform SpawnPoint;
public Transform Destination1;
public Transform Destination2;
public Transform Destination3;
// Use this for initialization
void Start()
{
Agent.SetActive(false);
InvokeRepeating("Spawn", 0f, 2f);
}
void Spawn() {
var newAgent = Instantiate(Agent);
newAgent.GetComponent<NavAgentBehaviour>().Destinations = new[] { Destination1, Destination2, Destination3 };
newAgent.transform.position = SpawnPoint.position;
newAgent.SetActive(true);
}
}
Increase the number of destination, to make the moves look more random. By the way, the destinations do not need to be specified by game objects, which is only for making it easy to see the sample program's behaviour.
The source of the jitteriness comes from the fact that you are updating the position to move every frame so your objects never have a consistent location to move to. I would instead suggest attaching a new script to each of your objects that individually handles their movement. In that script you could do something like the following, which has a delay to keep the target position for more than 1 frame.
float delaytimer;
Vector3 pos;
void Start () {
getNewPosition(); // get initial targetpos
}
void Update () {
delaytimer += Time.deltaTime;
if (delaytimer > 1) // time to wait
{
getNewPosition(); //get new position every 1 second
delaytimer = 0f; // reset timer
}
transform.position = Vector3.MoveTowards(transform.position, pos, .1f);
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
You are changing the direction they are moving in every frame, thats what is causing the jitter.
You could wait a few moments before you change the direction, perhaps something like this.
// Outside update
float betweenChanges = 2;
float lastChange = 0;
// Inside update
if(Time.realtimeSinceStartup > lastChange)
{
// Change directions
// ...
lastChange = Time.realTimeSinceStart + betweenChanges;
}
You could also solve this by using InvokeRepeating or a Coroutine.
If you dont want all the NPC's to change direction at the same time and still plan on controlling every NPC from the same class like in your example, you should perhaps add a timer for each NPC instance instead and use that to decide when to change its direction.
A better idea would be to let each NPC have its own Movement-script.