I am fairly new to unity development. I want to find out why when I move a sprite object, it is causing very noticeable jitter. I have read quiet some answers on the internet, some say things about rigidbody2d, some about fixed or late update etc etc. But my problem does not seem to be solved by any of those. I just have a sprite object, no rigidbody, just a gameobject with sprite renderer component and a script attached to it in an empty project containing a camera ofcourse. Here is the whole script I am using, you can just copy paste into a cs file named MoveBackground and it will work.
using UnityEngine;
using System.Collections;
public class MoveBackground : MonoBehaviour {
Vector3 pointA;
Vector3 pointB;
float timeRequired;
public float speed = 5;
IEnumerator movingEnum;
float belowEdge;
// Use this for initialization
void Start () {
Camera cam = Camera.main;
float height = 2f * cam.orthographicSize;
belowEdge = cam.transform.position.y - height/2f;
movingEnum = StartMoving ();
initializeAndMoveObject ();
}
// Update is called once per frame
void Update () {
// transform.Translate(0, (-1*speed * Time.deltaTime) / 100, 0);
//this.gameObject.transform.Translate (0f , Time.deltaTime * speed,0f);
}
void MoveToNextPosition()
{
float distance = Mathf.Abs( Mathf.Sqrt (Mathf.Pow ((pointA.x - pointB.x), 2f) + Mathf.Pow ((pointA.y - pointB.y), 2f)));
float time = distance/speed;
timeRequired = time;
movingEnum.MoveNext ();
}
IEnumerator StartMoving()
{
while (true) {
yield return StartCoroutine(MoveObject(transform, pointA, pointB, timeRequired));
}
}
IEnumerator MoveObject(Transform thisTransform, Vector3 startPos, Vector3 endPos, float time)
{
var i= 0.0f;
var rate= 1.0f/time;
while (i < 1.0f) {
i += Time.deltaTime * rate;
thisTransform.position = Vector3.Lerp(startPos, endPos, i);
yield return null;
}
}
public void initializeAndMoveObject()
{
Vector3 moveTowardsPoint;
SpriteRenderer sR = GetComponent<SpriteRenderer> ();
Vector3 bounds = sR.bounds.extents;
float height = bounds.y * 2f;
moveTowardsPoint = transform.position;
moveTowardsPoint.y = belowEdge - height / 2f;
pointA = transform.position;
pointB = moveTowardsPoint;
MoveToNextPosition ();
}
}
This script moves the object from top to bottom. Also, I dont have an jitter when I run it on my computer in editor. But when I run it on device (iPhone6+) , it causes very very big annoying jitter, constantly, depending on how fast it is moving down.
This is not the only method I have tried to make an object move, I have tried using
transform.Translate(0, (-1*speed * Time.deltaTime) / 100, 0);
in update and fixedupdate to no avail. I have tried using Quad for this purpose. (My gameobject just basically is a Bg that moves down). Piece of code for quad script is
public float speed = -0.5f;
private Vector2 savedOffset;
void Start () {
savedOffset = renderer.sharedMaterial.GetTextureOffset ("_MainTex");
}
void Update () {
float y = Mathf.Repeat ((Time.time * speed), 1);
Vector2 offset = new Vector2 (savedOffset.x, y);
renderer.sharedMaterial.SetTextureOffset ("_MainTex", offset);
}
void OnDisable () {
renderer.sharedMaterial.SetTextureOffset ("_MainTex", savedOffset);
}
If some one can guide me, I would be very thank full to you.
This issue for me was solved by setting the targetFrameRate in the code to 60 for iPhone6+(setting 30 didnt work, setting -1 didnt work), any value above 60 works too but below 60 was causing jittering. So all I changed was, I had to execute this statement once my app started (Anywhere in the code will work but preferably Awake, in my scenario it didnt had to be in Awake).
Application.targetFrameRate = 60;
Unity documentation reference : http://docs.unity3d.com/ScriptReference/Application-targetFrameRate.html
Just a side note, not all devices require 60 framerate.
Related
I have two game objects on the Scene, 1.Character(Parent Object) and 2.Weapon(Child Object) . The problem is when the character is moving to the right side, the rotation of the weapon is fine, it is toward where character is facing and rotating as expected, as you can see in the Gif image attach below. But when i Flip to left side everything goes wrong, the weapon goes backward and when i press down arrow the rotation goes up and when press up arrow the rotation goes down, see the Gif image attach below.. Please help How to Fix it.
Here is my Code:
public float weaponRotationSpeed = 13f;
private Animator anim;
private float angle;
void Awake()
{
anim = GetComponent<Animator>();
}
void Update()
{
Vector2 hv = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
Vector3 changeParentScale = transform.localScale;
if (hv != Vector2.zero)
{
if (Input.GetAxis("Horizontal") < 0)
{
changeParentScale.x = -5f;
transform.localScale = changeParentScale;
}
else if (Input.GetAxis("Horizontal") > 0)
{
changeParentScale.x = 5f;
transform.localScale = changeParentScale;
}
angle = Mathf.Atan2(hv.y, hv.x) * Mathf.Rad2Deg;
transform.Find("Weapon").rotation = Quaternion.Lerp(transform.Find("Weapon").rotation,
Quaternion.Euler(0, 0, angle),
weaponRotationSpeed * Time.deltaTime);
anim.SetBool("isRunning", true);
}
else
{
anim.SetBool("isRunning", false);
}
well you probably would want to flip the rotation then as well? E.g. using Mathf.Sign
Quaternion.Euler(0,0, angle * Mathf.Sign(changeParentScale.x))
There are some other little flaws in your code!
You shouldn't use Find each frame .. rather store it once.
You are using GetAxis repeatedly and should also store the vaues the first time .. don't you already have them in hv?
Lerp is quite cool tool but you are using it wrong ;) It interpolates each frame using the given factor .. usually you want a fixed one like e.g. 0.5f and not using Time.deltaTime
It should probably rather be something like
// You will have to adjust this value again
// This needs to be a constant value between 0 and 1
// - 0: rotation isn't updated at all
// - 1: rotation immediately jumps to target
// - e.g. 0.5f: rotation is every frame set to the middle between current and target
[Range(0f, 1f)]
public float weaponRotationSpeed = 0.5f;
// already reference these via the Inspector
[SerializeField] private Animator anim;
[SerializeField] private Transform weapon;
private float angle;
// As Fallback get them ONCE on runtime
void Awake()
{
if(!anim) anim = GetComponent<Animator>();
if(!weapon) weapon = transform.Find("Weapon");
}
void Update()
{
Vector2 hv = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
Vector3 changeParentScale = transform.localScale;
if (hv != Vector2.zero)
{
// get direction of Horizontal
int sign = Mathf.Sign(hv.x);
if (!Mathf.Approximately(hv.x, 0))
{
changeParentScale.x = 5f * sign;
transform.localScale = changeParentScale;
}
angle = Mathf.Atan2(hv.y, hv.x) * Mathf.Rad2Deg;
weapon.rotation = Quaternion.Lerp(weapon.rotation, Quaternion.Euler(0, 0, angle * sign), weaponRotationSpeed);
anim.SetBool("isRunning", true);
}
else
{
anim.SetBool("isRunning", false);
}
}
Alternatively you could probably also already solve it by not using rotation but localRotation for rotating the weapon.
I would like to enable gyro controlled camera onButtonClick event but I want it to start at the camera's current position. Currently when the gyro gets enabled it moves the camera off to a new position (probably the devices current gyro rotation) rather than leaving it where it is and gyro-ing from that point.
Hope I'm making sense, but basically I don't want the user to notice any change in what they are seeing in the game (ie. camera controlled by gyro but not that user would notice that change). Here's the code I'm using:
void Update ()
{
Quaternion attitudeFix = new Quaternion (-gyro.attitude.x, -gyro.attitude.z, -gyro.attitude.y, gyro.attitude.w);
Quaternion offsetRotation = initialGyroRotation * attitudeFix;
rotation = initialRotation * offsetRotation;
transform.rotation = rotation;
}
public void EnableGyro()
{
initialGyroRotation = Input.gyro.attitude;
initialRotation = transform.rotation;
Debug.Log("initialRotation: " + initialRotation.ToString());
Debug.Log("transform.rotation: " + transform.rotation.ToString());
Debug.Log("initialGyroRotation: " + initialGyroRotation.ToString());
}
**
EDIT: Here's a screen of exactly how I want the view to look as the user is holding their device in front of their face (portrait) AND heading north. Regardless of the orientation of the device when the app starts, this is how it should look when heading north with phone in portrait orientation (again as the user is looking through the phone).
EDIT 2:
Tests were getting confusing so I put the code back to exactly how your solution suggests. There is still a slight problem, but it seems like this script is very close. The main problem is the screen doesn't look like the above pic when I run each test, starting the app with the device on strange angles. It really shouldn't matter what angle the device is when the app is started, it needs to look like the above screen when pointing north and portrait.
I need to do more tests, and will do so with a new/clean project.
You need to get the offset camera position in the Awake or Start function. In the Update function, apply that offset value to the value from the gyro sensor.
It looks like you already know offset should be used but you are not doing that the right way. The confusing part is getting the offset which requires subtracting the current camera rotation from the gyro sensor value.
To subtract a Quaternion multiply the Inverse not just the Quaternion:
Quaternion = Quaternion *Quaternion.Inverse
To add a Quaternion multiply the Quaternion:
Quaternion = Quaternion * Quaternion.
This is what your code should look like:
Quaternion offset;
void Awake()
{
Input.gyro.enabled = true;
}
void Start()
{
//Subtract Quaternion
offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
}
void Update()
{
GyroModifyCamera();
}
void GyroModifyCamera()
{
//Apply offset
transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
I hade trouble with this and it wasn't working in build this is what I found and this script has worked.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GyroMovement : MonoBehaviour
{
// Start is called before the first frame update
//Movement
[SerializeField] float speed = 3.3f;
private float rotspeed = 90f;
private Vector3 moveDirection = Vector3.zero;
[SerializeField] private CharacterController controller;
//Rotation
private float _initialYAngle = 0f;
private float _appliedGyroYAngle = 0f;
private float _calibrationYAngle = 0f;
private Transform _rawGyroRotation;
private float _tempSmoothing;
//settings
private float _smoothing = 0.1f;
private void Update(){
//Movement
//Vector3 move = new Vector3 (Input.acceleration.x * speed * Time.deltaTime, 0f, -Input.acceleration.z * speed * Time.deltaTime);
//Vector3 rotMovement = transform.TransformDirection(move);
//controller.Move(rotMovement);
//Rotation
ApplyGyroRotation();
ApplyCalibration();
transform.rotation = Quaternion.Slerp(transform.rotation, _rawGyroRotation.rotation, _smoothing);
}
private IEnumerator Start(){
Input.gyro.enabled = true;
Application.targetFrameRate = 60;
_initialYAngle = transform.eulerAngles.y;
_rawGyroRotation = new GameObject("GyroRaw").transform;
_rawGyroRotation.position = transform.position;
_rawGyroRotation.rotation = transform.rotation;
yield return new WaitForSeconds(1f);
StartCoroutine (CalibrateYAngle());
}
private IEnumerator CalibrateYAngle(){
_tempSmoothing = _smoothing;
_smoothing = 1f;
_calibrationYAngle = _appliedGyroYAngle - _initialYAngle;
yield return null;
_smoothing = _tempSmoothing;
}
private void ApplyGyroRotation(){
_rawGyroRotation.rotation = Input.gyro.attitude;
_rawGyroRotation.Rotate(0f, 0f, 180f, Space.Self);
_rawGyroRotation.Rotate(90f, 180f, 0f, Space.World);
_appliedGyroYAngle = _rawGyroRotation.eulerAngles.y;
}
private void ApplyCalibration(){
_rawGyroRotation.Rotate(0f, -_calibrationYAngle, 0f, Space.World);
}
public void SetEnabled (bool value){
enabled = true;
StartCoroutine(CalibrateYAngle());
}
}
I would like to enable gyro controlled camera onButtonClick event but I want it to start at the camera's current position. Currently when the gyro gets enabled it moves the camera off to a new position (probably the devices current gyro rotation) rather than leaving it where it is and gyro-ing from that point.
Hope I'm making sense, but basically I don't want the user to notice any change in what they are seeing in the game (ie. camera controlled by gyro but not that user would notice that change). Here's the code I'm using:
void Update ()
{
Quaternion attitudeFix = new Quaternion (-gyro.attitude.x, -gyro.attitude.z, -gyro.attitude.y, gyro.attitude.w);
Quaternion offsetRotation = initialGyroRotation * attitudeFix;
rotation = initialRotation * offsetRotation;
transform.rotation = rotation;
}
public void EnableGyro()
{
initialGyroRotation = Input.gyro.attitude;
initialRotation = transform.rotation;
Debug.Log("initialRotation: " + initialRotation.ToString());
Debug.Log("transform.rotation: " + transform.rotation.ToString());
Debug.Log("initialGyroRotation: " + initialGyroRotation.ToString());
}
**
EDIT: Here's a screen of exactly how I want the view to look as the user is holding their device in front of their face (portrait) AND heading north. Regardless of the orientation of the device when the app starts, this is how it should look when heading north with phone in portrait orientation (again as the user is looking through the phone).
EDIT 2:
Tests were getting confusing so I put the code back to exactly how your solution suggests. There is still a slight problem, but it seems like this script is very close. The main problem is the screen doesn't look like the above pic when I run each test, starting the app with the device on strange angles. It really shouldn't matter what angle the device is when the app is started, it needs to look like the above screen when pointing north and portrait.
I need to do more tests, and will do so with a new/clean project.
You need to get the offset camera position in the Awake or Start function. In the Update function, apply that offset value to the value from the gyro sensor.
It looks like you already know offset should be used but you are not doing that the right way. The confusing part is getting the offset which requires subtracting the current camera rotation from the gyro sensor value.
To subtract a Quaternion multiply the Inverse not just the Quaternion:
Quaternion = Quaternion *Quaternion.Inverse
To add a Quaternion multiply the Quaternion:
Quaternion = Quaternion * Quaternion.
This is what your code should look like:
Quaternion offset;
void Awake()
{
Input.gyro.enabled = true;
}
void Start()
{
//Subtract Quaternion
offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
}
void Update()
{
GyroModifyCamera();
}
void GyroModifyCamera()
{
//Apply offset
transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
I hade trouble with this and it wasn't working in build this is what I found and this script has worked.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GyroMovement : MonoBehaviour
{
// Start is called before the first frame update
//Movement
[SerializeField] float speed = 3.3f;
private float rotspeed = 90f;
private Vector3 moveDirection = Vector3.zero;
[SerializeField] private CharacterController controller;
//Rotation
private float _initialYAngle = 0f;
private float _appliedGyroYAngle = 0f;
private float _calibrationYAngle = 0f;
private Transform _rawGyroRotation;
private float _tempSmoothing;
//settings
private float _smoothing = 0.1f;
private void Update(){
//Movement
//Vector3 move = new Vector3 (Input.acceleration.x * speed * Time.deltaTime, 0f, -Input.acceleration.z * speed * Time.deltaTime);
//Vector3 rotMovement = transform.TransformDirection(move);
//controller.Move(rotMovement);
//Rotation
ApplyGyroRotation();
ApplyCalibration();
transform.rotation = Quaternion.Slerp(transform.rotation, _rawGyroRotation.rotation, _smoothing);
}
private IEnumerator Start(){
Input.gyro.enabled = true;
Application.targetFrameRate = 60;
_initialYAngle = transform.eulerAngles.y;
_rawGyroRotation = new GameObject("GyroRaw").transform;
_rawGyroRotation.position = transform.position;
_rawGyroRotation.rotation = transform.rotation;
yield return new WaitForSeconds(1f);
StartCoroutine (CalibrateYAngle());
}
private IEnumerator CalibrateYAngle(){
_tempSmoothing = _smoothing;
_smoothing = 1f;
_calibrationYAngle = _appliedGyroYAngle - _initialYAngle;
yield return null;
_smoothing = _tempSmoothing;
}
private void ApplyGyroRotation(){
_rawGyroRotation.rotation = Input.gyro.attitude;
_rawGyroRotation.Rotate(0f, 0f, 180f, Space.Self);
_rawGyroRotation.Rotate(90f, 180f, 0f, Space.World);
_appliedGyroYAngle = _rawGyroRotation.eulerAngles.y;
}
private void ApplyCalibration(){
_rawGyroRotation.Rotate(0f, -_calibrationYAngle, 0f, Space.World);
}
public void SetEnabled (bool value){
enabled = true;
StartCoroutine(CalibrateYAngle());
}
}
I have the following code for my 2D game, it makes object randomly wonder on the screen. What I am having issues with, is when an object looks at the point it is going to, I would like it to rotate as it moves forward. What is happening now, is it rotates instantly to the point it is going towards. So, how can I get it to rotate slowly and move forward at the same time?
using UnityEngine;
using System.Collections;
public class Wonder : MonoBehaviour {
protected Vector2 wayPoint;
protected float speed;
// Use this for initialization
void Start () {
speed = gameObject.GetComponent<Move>().playerSpeed;
wonder();
}
void wonder(){
wayPoint = Random.insideUnitCircle * 10;
}
// Update is called once per frame
void Update () {
Vector2 dir = wayPoint - new Vector2(transform.position.x, transform.position.y);
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(new Vector3(0, 0,Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg - 90));
transform.position = Vector2.MoveTowards(transform.position, wayPoint, Time.deltaTime * speed);
float magnitude = (new Vector2(transform.position.x, transform.position.y) - wayPoint).magnitude;
if(magnitude < 3){
wonder();
}
}
}
Here is an example Image:
So, once the ship gets to its point another will be created and it will move there. I am thinking I will have to have a list of 5+ points, then calculate the arch the ship needs to take adding new points as the ship hits a way point then removing old ones after. I am not sure how to do this though...
using UnityEngine;
using System.Collections;
public class Wander : MonoBehaviour {
protected Vector3 velocity;
protected Vector2 waypoint;
protected float speed;
// Use this for initialization
void Start () {
speed = gameObject.GetComponent<Move>().playerSpeed;
RandomizeWaypoint();
}
void RandomizeWaypoint(){
waypoint = Random.insideUnitCircle * 10;
}
// Update is called once per frame
void Update () {
transform.position = Vector3.SmoothDamp( transform.position, waypoint, ref velocity, Time.deltaTime * speed );
transform.rotation = Quaternion.AngleAxis( Mathf.Atan2( velocity.y, velocity.x ) * Mathf.Rad2Deg, Vector3.forward );
if( Vector3.Distance( transform.position, waypoint ) < 3 ){
RandomizeWaypoint();
}
}
}
Untested. Vector3.SmoothDamp can be pretty handy. Note the spelling also.
i have a simple script that should animate my player but its not working. i read forums for a while, some had issues about animation legacy option and i fixed it, but my character still doesn't animate, and there isn't any compiling errors. Here is my script:
using UnityEngine;
using System.Collections;
public class maincharacter : MonoBehaviour {
void Start () {
}
float xSpeed = 10f;
float zSpeed = 10f;
public float playerMovementSpeed = 10f;
void Update () {
float deltaX = Input.GetAxis ("Horizontal") * xSpeed;
float deltaZ = Input.GetAxis ("Vertical") * zSpeed;
Vector3 trans = new Vector3 (deltaX + deltaZ ,0f,deltaZ - deltaX);
transform.Translate (trans.normalized * Time.deltaTime * playerMovementSpeed, Space.World);
animation.Play ("mcrunsw");
/*if (deltaX != 0 || deltaZ != 0) {
animation.Play ("mcrunsw");
}*/
}
}
Here is my gameObject and animation:
Any help would be appreciated. Thanks in advance.
From the manual:
Play() will start animation with name animation, or play the default
animation. The animation will be played abruptly without any blending.
Since you call this every frame, I'd suppose it will just show the first frame of the animation and then be stopped by the next animation.Play in the next Update. Try this:
if (!animation.isPlaying) {
animation.Play();
}
In general, I would suggest using Mechanim for character animations.
Another viable option along with the solution above is CrossFadeQueued.
You just simply use the function below and it'll seamlessly CrossFade.
usage is PlayAnimation("mcrunsw");
function PlayAnimation(AnimName : String) {
if (!animation.IsPlaying(AnimName))
animation.CrossFadeQueued(AnimName, 0.3, QueueMode.PlayNow);
}