Player Leg Moving with transform.rotation = Quaternion.AngleAxis - c#

I am trying to get my Player prefabs leg and shoe to move "Forward" world space wise without my shoe on each leg rotating. I just want it pointing forward.
I have a player prefab. On the prefab I have a script, two gameobjects with a cylinder in each one acting as a "leg", and another two gameobject with a shoe model I made in each gameobject. the shoe gameobject is inside the leg gameobject. so for example it goes:
Player prefab
-> Right Leg Gameobject
-> Left Leg Gameobject
-> RightShoe Gameobject
-> Left Shoe Gameobject
and under each gameopbject is the respective models for the leg or shoe.
I have written code so that my player's legs "move"/"change rotation" to make it look like it's walking (I don't know any animation so this is the only way I know how). There is also code so that moving player is with AWSD and looking or rotating player is with the mouse, just like any typical FPS game, except my game in 3rd person.
My player's leg does move "forward" relative to world space so that is not an issue but hen I use the mouse to rotate or look in another direction (left or right), the players shoes also rotate left or right in place. At first I thought something was wrong with the shoes but I did not write any code to the shoes, I only wrote code for the legs. Since my legs were cylindrical, I did not notice the leg rotates too. I only found this out once I set my cylindrical legs to be more oblong or more egg shaped like.
is there a way I can make my shoes do the same "animation" as my leg but not make it rotate in place?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class PlayerController : NetworkBehaviour
{
public float speedH = 2.0f;
private float yaw = 0.0f;
public float WalkingTime; //timer for walking animation
public GameObject PlayerLeftLeg;
public GameObject PlayerRightLeg;
private float PlayerStatMenuTimer;
public GameObject PlayerStatsMenu;
public GameObject ThePlayer;
// Update is called once per frame
void Update () {
if (!isLocalPlayer)
{
return;
}
//keep track of time for player stat menu
//if not here than menua will show and hide like a thousand times when pressed once due to update reading code per frame
PlayerStatMenuTimer = PlayerStatMenuTimer + 1 * Time.deltaTime;
//moving player left right forward backward
var x = Input.GetAxis("Horizontal") * Time.deltaTime * 50.0f;
var z = Input.GetAxis("Vertical") * Time.deltaTime * 50.0f;
transform.Translate(x, 0, z);
//rotating player or "Looking"
yaw += speedH * Input.GetAxis("Mouse X");
transform.eulerAngles = new Vector3(0.0f, yaw, 0.0f);
//if player is using WASD to move then do leg moving animation
//if not moving then set legs to be still and reset in standing position
//FYI: "transform.TransformVector(1,0,0)" was used instead of "Vector3.forward" was because
// vector3.forward is local space, so when i rotate player the sense of "forward" also changes, thus i needed
// a code that uses the world space, thus i used "transform.TransformVector(1,0,0)"
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
{
CmdWalk();
RpcWalk();
}
else
{
//if player not walking then reset
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
WalkingTime = 0;
}
//get hidden mouse pointer back and unlock
if (Input.GetKey(KeyCode.Escape))
{
Cursor.lockState = CursorLockMode.None;
}
//opens and closes stat menu
if (Input.GetKey(KeyCode.Return) && (PlayerStatMenuTimer>=1) && (PlayerStatsMenu.activeSelf==false))
{
Cursor.lockState = CursorLockMode.None;
PlayerStatsMenu.SetActive(true);
PlayerStatMenuTimer = 0;
//call the script "GetplayerStats" and call function "retrieceplayerstats"
var GetStats = GetComponent<GetPlayerStats>();
GetStats.RetrievePlayerStats();
}
else if (Input.GetKey(KeyCode.Return) && PlayerStatMenuTimer >= 1 && PlayerStatsMenu == true)
{
Cursor.lockState = CursorLockMode.Locked;
PlayerStatsMenu.SetActive(false);
PlayerStatMenuTimer = 0;
}
}
private void Awake()
{
//this code locks mouse onto center of window
//Screen.lockCursor = true;
Cursor.lockState = CursorLockMode.Locked;
}
//initiaztes when started up
void Start()
{
//calls script "SpawnItems" and function "RefreshItems" which will update the players items being shown
ThePlayer.GetComponent<SpawnItems>().RefreshItems();
}
//so COMMAND is for server to client
//it shows walking for local player
[Command]
void CmdWalk()
{
//timer
WalkingTime += Time.deltaTime;
//right leg stepping forward
if (WalkingTime > 0 && WalkingTime < .4)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
}
//left leg stepping forward
if (WalkingTime > .4 && WalkingTime < 1.2)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
}
//right leg stepping forward
if (WalkingTime > 1.2 && WalkingTime < 1.59)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
}
//resetting
if (WalkingTime > 1.6)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
WalkingTime = 0;
}
}
//so RPC is for Client to Server
//it shows walking for other client players
//https://stackoverflow.com/questions/53784897/unity-moving-player-leg-multiplayer
[ClientRpc]
void RpcWalk()
{
//timer
WalkingTime += Time.deltaTime;
//right leg stepping forward
if (WalkingTime > 0 && WalkingTime < .4)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
}
//left leg stepping forward
if (WalkingTime > .4 && WalkingTime < 1.2)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
}
//right leg stepping forward
if (WalkingTime > 1.2 && WalkingTime < 1.59)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
}
//resetting
if (WalkingTime > 1.6)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
WalkingTime = 0;
}
}
}
I have included the entire script for my player controller, all you need to focus on is the CMDWALK or RPC WALK, they are both the same piece of code.
if anyone needs more info on the leg movement world space, take a look at this link, it is another question I asked Unity Moving Player Leg Multiplayer

The problem is you are mixing Euler angles with Quaternion values. This is never a good idea. While an Euler angles can be uniquely represent in Quaternion space, the other way round one Quaternion has multiple representations in Euler space. Therefore your direct access of rotation.x is not reliable (at least not for using it than in AngleAxis.
Additionally you are dealing with global rotation here. You should use local rotations instead to avoid the problems with nested rotated objects.
However in your case you should as already said simply use
transform.Rotate(60 * WalkingTime, 0, 0, Space.Self);
and for a reset simply
transform.localRotation = Quaternion.Identity;
Btw as I mentioned in my answer(the "Update" section) to your previous question don't forget to skip the ClientRpc if you are the server or the client who originally invoked the call to avoid duplicate movements. And don't call both, Cmd and Rpc at the same place this could lead to strange behaviours since the Rpc can only be called by the server. Call one method walk and from there go on with the Cmd which than Invokes the Rpc.

Related

How do I make my character movement less "buggy"

Whenever I'm running against a wall, like if there is a wall to my left and I hold 'a' against the wall it kinda spazes out. It looks like the character is going in and out of the wall. Hopefully that made sense and you know what I'm talking about. So my question is how could I fix this so that when I am actively running into a wall it doesnt do that and instead the character is just there against the wall and appearing to move at all.
code for the movement:
void Update()
{
var movement = Input.GetAxis("Horizontal");
transform.position += new Vector3(movement, 0, 0) * Time.deltaTime * MovementSpeed;
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
Setting the transform.position literally teleports the player, so it sometimes teleports them into the wall then they get pushed back.
To prevent that i suggest using the rigidbody's movePosition function. This takes into account physics while moving, so it interacts with the other objects that are there.
To change your current code to that it would be something like this:
void Update()
{
var movement = Input.GetAxis("Horizontal");
//This moves the GameObject to the currentPosition + The move direction. Which means to move it in the direction that you intended to move in.
_rigidbody.MovePosition(transform.position + new Vector3(movement, 0, 0) * Time.deltaTime * movementSpeed);
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
However i suggest splitting it into this:
float movement = 0f; //Setting a default value.
void Update()
{
movement = Input.GetAxis("Horizontal"); //Getting movement input from player
if (Input.GetButtonDown("Jump") && Mathf.Abs(_rigidbody.velocity.y) < .001f)
{
_rigidbody.AddForce(new Vector2(0, JumpForce), ForceMode2D.Impulse);
}
}
private void FixedUpdate()
{ //Moving in Physics update
_rigidbody.MovePosition(transform.position + new Vector3(movement, 0, 0) * Time.deltaTime * movementSpeed);
}
Because Update checks every frame, so its good for checking input, however you should apply physics in FixedUpdate(), because that is applied whenever there is a physics update (Or physics frame). (For even more smoothness!)

Direction entity movment glitch in unity 2d

I am creating a game in Unity 2D. I have Dragons that I am adding to the scene. The dragons are only supposed to move in 1 of 4 directions, Up, Down, Left and Right. the dragons that move left and right move exactly as intended. However the dragons that move up and down have a problem in that they move at an angle. All dragons that move upwards move up and to the right at a 45 degree angle. All dragons that move downwards move down and to the left at a 45 degree angle.
at first I thought it was a problem with the animator moving the dragon to a different location, but I removed the animator component from the prefab and the problem still persisted.
below is the code I am using to move the dragons.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragonMovment : MonoBehaviour {
public string Direction; //needs to be set in the prefab
public float DragonSpeed; //speed of dragon
Rigidbody2D rb;
public Transform Boundries;
// Use this for initialization
void Start ()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void FixedUpdate ()
{
float MoveRight = 1;
float MoveLeft = -1;
float MoveUp = 1;
float MoveDown = -1;
if (Direction== "R")
{
rb.velocity = new Vector3(DragonSpeed * MoveRight, rb.velocity.y);
}
if (Direction == "L")
{
rb.velocity = new Vector3(DragonSpeed * MoveLeft, rb.velocity.y);
}
if (Direction == "U")
{
rb.velocity = new Vector3(DragonSpeed * MoveUp, rb.velocity.x);
}
if (Direction == "D")
{
rb.velocity = new Vector3(DragonSpeed * MoveDown, rb.velocity.x);
}
}
}
Edit.
So why does the following work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControler : MonoBehaviour {
// speed of movment
public float Speed;
// rb
Rigidbody2D rb;
public Transform Boundries;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
// get horizontal input
float MoveSide = Input.GetAxis("Horizontal");
//get vertical input
float MoveVert = Input.GetAxis("Vertical");
// move horizontal
rb.velocity = new Vector3(Speed * MoveVert, rb.velocity.y);
// move vertical
rb.velocity = new Vector3(Speed * MoveSide, rb.velocity.x);
}
}
but the other code doesent?
You have got the velocity x in the y of the vector 3
if (Direction == "U")
{
rb.velocity = new Vector3(rb.velocity.x, DragonSpeed * MoveUp);
}
if (Direction == "D")
{
rb.velocity = new Vector3(rb.velocity.x, DragonSpeed * MoveDown);
}
It works in your player script as you are overriding the values in the subsequent statement.
float MoveSide = Input.GetAxis("Horizontal"); //eg 1
float MoveVert = Input.GetAxis("Vertical"); // eg 1
// setting your x velocity incorrectly to the y (vert) velocity speed and keeping y the same velocity as start of frame
rb.velocity = new Vector3(Speed * MoveVert, rb.velocity.y);
// Set the y to the x value of the statement above so it is now in the correct vector and set the x to the correct hoz velocity
rb.velocity = new Vector3(Speed * MoveSide, rb.velocity.x);
// effectively doing
rb.velocity = new Vector3(Speed * MoveSide, Speed * MoveVert);
You should also be using MovePosition as it doesn't directly affect the physics engine (using velocity can have knock on effects to collisions and triggers and create unexpected physics). Your gameobjects will have to be marked as kinematic otherwise the below will cause them to teleport to the new position instantly.
var movementDirection = new Vector3(Speed * MoveSide, Speed * MoveVert);
rb.MovePosition(transform.position + movementDirection * Time.deltaTime);
And the * Time.deltaTime ensures that movement is consistent for different framerates. If you run the game on a 30 fps machine the game objects will move slower than a 60fps. Time.deltaTime calculates the physical time passed since the previous frame and ensures the distance traveled is the same regardless of frame rate.
e.g say the gameObject moves 1 per frame update. After a second on a 30 fps machine the object would have moved 30. After a second on a 60 fps machine the object would have moved 60.
Time.deltaTime=.2s on 30 fps so 1 movement * .2 = move .2 per frame * 30 frames in the second = 60 moved
Time.deltaTime=.1s on 60 fps so 1 movement * .1 = move .1 per frame * 60 frames in the second = 60 moved

Unity how to allow object to move within a certain boundary using only coordinates

I want to script a cube so that it can only move from 0-5 on the x axis.
My cube starts at zero. I want my cube to move forward until it reaches 5, then stop and go backwards until it reaches 0, then repeat.
The problem I am getting is of course my cube stops at 5, moves back 1 step to 4 and of course it is allowed move forward again. I want it to go 0-5, then 5-0 and repeat.
My Attempt
//Drags cube along the ground
if (transform.position.x > 0 & transform.position.x < 5) {
transform.Translate(1* Time.deltaTime, 0, 0);
}
//If our cubes reaches our boundary, move it backwards
if (transform.position.x >= 5 || transform.position.x <= 0 ) {
transform.Translate(-1, 0, 0);
}
Use Mathf.PingPong to oscillate between 0f and 5f based on the current time.
float speed = 1f;
float curXPosition = Mathf.PingPong(speed * Time.time, 5f);
transform.position = new Vector3(curXPosition,transform.position.y,transform.position.z);
If you need it to start at 0 at a specific time:
// as class field
public float zeroTime=0f;
public float speed;
// When you want it to start from zero, call this line:
this.zeroTime = Time.time;
// in Update/FixedUpdate
float curXPosition = Mathf.PingPong(speed * (Time.time-zeroTime), 5f);
transform.position = new Vector3(curXPosition,transform.position.y,transform.position.z);

Understanding Unity eulerAngles (how to limit rotation of a 2D object to 45 degrees?)

Please keep in mind I am a complete beginner.
I am trying to make a space shooter game. I have a 2D sprite facing upwards and this code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 5.0f;
private void Start()
{
transform.position = Vector3.zero;
}
private void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
Vector3 eulerAngles = transform.rotation.eulerAngles;
//Debug.Log("transform.rotation angles x: " + eulerAngles.x + " y: " + eulerAngles.y + " z: " + eulerAngles.z);
transform.Translate(Vector3.right * speed * horizontalInput * Time.deltaTime, Space.World);
transform.Translate(Vector3.up * speed * verticalInput * Time.deltaTime, Space.World);
if (horizontalInput > 0)
{
transform.Rotate(new Vector3(0, 0, -1) * horizontalInput * Time.deltaTime * 300);
}
else if (horizontalInput < 0)
{
transform.Rotate(new Vector3(0, 0, -1) * horizontalInput * Time.deltaTime * 300);
}
if (eulerAngles.z > 45)
{
//transform.rotation = Quaternion.AngleAxis(45, Vector3.forward);
Debug.Log("45");
if (eulerAngles.z < -45)
{
//transform.rotation = Quaternion.AngleAxis(-45, Vector3.forward);
Debug.Log("-45");
}
For now, I want the log to say "45" when the value in Transform.rotation.z reaches 45, and "-45" when it reaches -45. My real intention (commented out in the code) is to then tell the sprite to stop rotation at 45 or -45 degrees.
When I press the left arrow until it rotates to 45, it works. But here's the problem... When I press right arrow, and the value of Transform.rotation.z becomes negative, and the log immediately says "45" as if any negative number is somehow superior to 45.
I notice that when I rotate the sprite in Unity, the values of Transform.rotation.z go on a sort of loop, until 180 then become negative until 0 and vice versa.
Could someone tell me what I'm missing? Am I overlooking other simpler, more effective ways to achieve what I'm looking for?
Thanks in advance for your help, I really appreciate it. I'm new and still struggling to grasp with many concepts.
I just solved it! I only had to set a range in the condition between 45 and 180, and for the other direction between 315 and 180.
if (eulerAngles.z > 45 && eulerAngles.z < 180)
{
transform.rotation = Quaternion.AngleAxis(-315, new Vector3(0, 0, 45));
Debug.Log("45");
}
if (eulerAngles.z < 315 && eulerAngles.z > 180)
{
transform.rotation = Quaternion.AngleAxis(-315, new Vector3(0, 0, -45));
Debug.Log("-45");
}
This makes it work perfectly. When the sprite (spaceship) is going sideways, it rotates to that side but stops rotating at 45 degrees.

Keeping my object from rolling out of the screen

I am using the tilt function on a phone to control an object to roll left and right. I do not want it roll anything beyond the screen width.
As in the simple illustration below, the dotted lines represent the width of the screen. The 'O' is the object and the Max signs indicate the maximum point the object is allowed to roll to.
Max--------O--------Max
But currently using my code, the object still rolls out of the screen. Also tried testing both height n width and ended up the same result where the object rolls out of the screen. Please advice what I am doing wrong. Thank you.
public float speed = 10.0F;
void Update()
{
Vector3 dir = Vector3.zero;
dir.x = Input.acceleration.x;
if (dir.sqrMagnitude > 1)
dir.Normalize();
dir *= Time.deltaTime;
if (!(Mathf.Round(dir.x) > Mathf.Round(Screen.width/2)) || !(Mathf.Round(dir.x) < -Mathf.Round(Screen.width/2)))
{
transform.Translate(dir * speed);
}
}
**Updated
public float speed = 10.0F;
void Update()
{
Vector3 dir = Vector3.zero;
dir.x = Input.acceleration.x;
if (dir.sqrMagnitude > 1)
dir.Normalize();
dir *= Time.deltaTime;
//transform.Translate(dir * speed);
if (transform.position.x < 0)
{
transform.position = new Vector3(0, this.transform.position.y, this.transform.position.z);
}
else if (transform.position.x > Screen.width)
{
transform.position = new Vector3(Screen.width, this.transform.position.y, this.transform.position.z);
}
else
{
transform.Translate(dir * speed);
}
}
I think thereare several ways you can go about checking the transfomrs position.
first off if you were using a 2d camera you could use a method like
leftBounds = Camera.x - (Camera.pixelWidth/2);
however because ortho camera angles are not set at any particulare size at x distace from camera they are hard to calculate.
i have seen some instances were coliders on the camera just outside the render rand were placed as camera children
adding a colision mask to only affect the appropriate game object would be best.

Categories

Resources