I am making a 2d game that can use either the keyboard or an Xbox One controller as movement.
So far, I can move the players and play a running animation, jump animation etc using the keyboard, and I can move using the Xbox controller.
However, I can't play animations with the Xbox controller.
I have this:
void Update()
{
movement.x = Input.GetAxis("LeftJoystickX") * speed * Time.deltaTime;
// Orange player movement
if (orange)
{
transform.position += movement;
if (Input.GetKey(KeyCode.D))
{
ChangeDirection("right");
ChangeState(STATE_RUN);
transform.position += Vector3.right * speed * Time.deltaTime;
if (!onGround)
ChangeState(STATE_JUMP);
}
}
}
Here, if you use the keyboard, the animations play. If you use the controller, the player moves but the animations don't play. I've tried and narrowed it down to the fact that this piece of code will not evaluate:
if (Input.GetAxis("LeftJoystickX"))
as Input.GetAxis() returns a float and not a bool (?) and therefore cannot be put in an if statement.
I guess what I am asking is, is it possible to detect a direction of a Joystick push from a controller, such as something like Input.GetAxis().joystickPushedRight?
Or better yet, is there a different way I could execute
if (Input.GetKey(KeyCode.D))
{
ChangeDirection("right");
ChangeState(STATE_RUN);
transform.position += Vector3.right * speed * Time.deltaTime;
if (!onGround)
ChangeState(STATE_JUMP);
}
But use the joystick instead of Input.GetKey?
Thanks!
Input.GetAxis returns a float between -1 and 1.
If you take that into account together with a threshold for values close to zero you can determine if you need to go up or down or need to stop.
Your implementation will look like this:
const float threshold = 0.05;
const string axis = "LeftJoystickX";
if (Input.GetAxis(axis) < -threshold)
{
//go left
}
else if (Input.GetAxis(axis) > threshold)
{
//go right
}
else
{
// don't move
}
Related
right now I am trying to make rts camera zooming with panning when close to the ground. The problem I have now is that I use mouse scroll wheel for zooming and it makes the zooming feel like it's laggy. It looks like it jumps some Y value and teleports rather than smoothly move to the desired position. Also, I would like to know how to make the camera stop at the minimum Y value because what is happening now is that it stops at around 22 rather than 20 which is my minimum Y value for the camera movement.
I tried zooming with + and - on my numpad and it worked how I wanted (smoothly zooming in and out without skipping) but not all players have numpad and I feel it is more suitable to zoom with mouse wheel.
{
private const int levelArea = 100;
private const int scrollArea = 25;
private const int scrollSpeed = 25;
private const int dragSpeed = 70;
private const int zoomSpeed = 50;
// Maximum/minimum zoom distance from the ground
public int zoomMin = 20;
public int zoomMax = 120;
private const int panSpeed = 40;
// Minimal/maximal angles for camera
private const int panAngleMin = 30;
private const int panAngleMax = 90;
void Update()
{
// Init camera translation for this frame.
var translation = Vector3.zero;
// Zoom in or out
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
if (zoomDelta != 0)
{
translation -= Vector3.up * zoomSpeed * zoomDelta;
}
// Start panning camera if zooming in close to the ground or if just zooming out.
var pan = transform.eulerAngles.x - zoomDelta * panSpeed;
pan = Mathf.Clamp(pan, panAngleMin, panAngleMax);
// When to start panning up the camera
if (zoomDelta < 0 || transform.position.y < (zoomMax -20))
{
transform.eulerAngles = new Vector3(pan, 0, 0);
}
// Move camera with arrow keys
translation += new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// Move camera with mouse
if (Input.GetMouseButton(2)) // MMB
{
// Hold button and drag camera around
translation -= new Vector3(Input.GetAxis("Mouse X") * dragSpeed * Time.deltaTime, 0,
Input.GetAxis("Mouse Y") * dragSpeed * Time.deltaTime);
}
else
{
// Move camera if mouse pointer reaches screen borders
if (Input.mousePosition.x < scrollArea)
{
translation += Vector3.right * -scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.x >= Screen.width - scrollArea)
{
translation += Vector3.right * scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.y < scrollArea)
{
translation += Vector3.forward * -scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.y > Screen.height - scrollArea)
{
translation += Vector3.forward * scrollSpeed * Time.deltaTime;
}
}
// Keep camera within level and zoom area
var desiredPosition = transform.position + translation;
if (desiredPosition.x < -levelArea || levelArea < desiredPosition.x)
{
translation.x = 0;
}
if (desiredPosition.y < zoomMin || zoomMax < desiredPosition.y)
{
translation.y = 0;
}
if (desiredPosition.z < -levelArea || levelArea < desiredPosition.z)
{
translation.z = 0;
}
// Move camera parallel to world axis
transform.position += translation;
}
}
I would like to have a smooth transition from the position where the camera is now and the desired position after scrolling in/out. And also I would like to know how to make the camera to stop at the minimum/maximum zoom distance rather than to stop close to it. Thank you for helping. Video how the camera movement looks like: https://youtu.be/Lt3atJEaOjA
Okay, so I'm going to do three things here. First and foremost, I'm going to recommend that if you're working with advanced camera behavior, you probably want to at least consider using Cinemachine. I'd walk you through it myself, but given my lack of personal experience with it, I'd probably be doing you a disservice by even trying. There are many good tutorials out there. Youtube and Google should provide.
The second thing I'll do is solve your problem in the most direct way I can manage, and after that, we'll see if we can't come up with a better method for solving your problem.
So, the key here is that Unity's scrollwheel input is pretty binary. When you check a scrollwheel axis, the result is directly based on how many "clicks" your wheel has gone through since the last frame update, but what you really want is something with a bit of give. By default, Unity can actually do this with most of its axis inputs: You might notice that if you use WASD in a default Unity project, there's a sort of "lag" to it, where you'll take your fingers off the keys but you'll still keep receiving positive values from Input.GetAxis() for a few frames. This is tied to the Gravity value in your input settings, and Input.GetAxisRaw() is actually used to circumvent this entirely. For whatever reason, scrollwheel axes don't seem to be affected by axis gravity, so we essentially have to implement something similar ourselves.
// Add this property to your class definition (so it persists between updates):
private float wheelAxis = 0;
// Delete this line:
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
// And put these three new lines in its place:
wheelAxis += Input.GetAxis("Mouse ScrollWheel");
wheelAxis = Mathf.MoveTowards(wheelTotal, 0f, Time.deltaTime);
var zoomDelta = Mathf.Clamp(wheelAxis, -0.05f, 0.05f) * zoomSpeed * Time.deltaTime;
Right, so we do a few things here. Every update, we add the current scrollwheel values to our wheelAxis. Next, we apply the current Time.deltatime as "gravity" via the Mathf.MoveTowards() function. Finally, we call what's mostly just your old zoomDelta code with a simple modification: We constrain the wheelAxis with Mathf.Clamp to try and regulate how fast the zooming can happen.
You can modify this code in a couple of ways. If you multiply the Time.deltaTime parameter you can affect how long your input will "persist" for. If you mess with the Mathf.Clamp() values, you'll get faster or slower zooming. All in all though, if you just want a smooth zoom with minimal changes to your code, that's probably your best bet.
So!
Now that we've done that, let's talk about your code, and how you're approaching the problem, and see if we can't find a somewhat cleaner solution.
Getting a good camera working is surprisingly non-trivial. Your code looks like a lot of code I see that tries to solve a complex problem: It looks like you added some feature, and then tested it, and found some edge cases where it fell apart, and then patched those cases, and then tried to implement a new feature on top of the old code, but it kinda broke in various other ways, etc etc, and what we've got at this point is a bit messy.
The biggest issue with your code is that the camera's position and the camera's rotation are closely tied together. When working with characters, this is usually fine, but when working with a camera, you want to break this up. Think about where the camera is and what the camera is looking at as very separate things to keep track of.
So here's a working camera script that you should be able to plug in and just run with:
using UnityEngine;
public class RTSCamera : MonoBehaviour
{
public float zoomSpeed = 100f;
public float zoomTime = 0.1f;
public float maxHeight = 100f;
public float minHeight = 20f;
public float focusHeight = 10f;
public float focusDistance = 20f;
public int panBorder = 25;
public float dragPanSpeed = 25f;
public float edgePanSpeed = 25f;
public float keyPanSpeed = 25f;
private float zoomVelocity = 0f;
private float targetHeight;
void Start()
{
// Start zoomed out
targetHeight = maxHeight;
}
void Update()
{
var newPosition = transform.position;
// First, calculate the height we want the camera to be at
targetHeight += Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * -1f;
targetHeight = Mathf.Clamp(targetHeight, minHeight, maxHeight);
// Then, interpolate smoothly towards that height
newPosition.y = Mathf.SmoothDamp(transform.position.y, targetHeight, ref zoomVelocity, zoomTime);
// Always pan the camera using the keys
var pan = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * keyPanSpeed * Time.deltaTime;
// Optionally pan the camera by either dragging with middle mouse or when the cursor touches the screen border
if (Input.GetMouseButton(2)) {
pan -= new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * dragPanSpeed * Time.deltaTime;
} else {
var border = Vector2.zero;
if (Input.mousePosition.x < panBorder) border.x -= 1f;
if (Input.mousePosition.x >= Screen.width - panBorder) border.x += 1f;
if (Input.mousePosition.y < panBorder) border.y -= 1f;
if (Input.mousePosition.y > Screen.height - panBorder) border.y += 1f;
pan += border * edgePanSpeed * Time.deltaTime;
}
newPosition.x += pan.x;
newPosition.z += pan.y;
var focusPosition = new Vector3(newPosition.x, focusHeight, newPosition.z + focusDistance);
transform.position = newPosition;
transform.LookAt(focusPosition);
}
}
While I encourage you to go through it in your own time, I'm not going to drag you through every inch of it. Instead, I'll just go over the main crux.
The key idea here is that rather than controlling the camera's height and orientation directly, we just let the scrollwheel dictate where want the camera's height to be, and then we use Mathf.SmoothDamp() to move the camera smoothly into that position over several frames. (Unity has many useful functions like this. Consider Mathf.MoveTowards() for an alternative interpolation method.) At the very end, rather than trying to fiddle with the camera's rotation values directly, we just pick a point in front of us near the ground and point the camera at that spot directly.
By keeping the camera's position and orientation completely independent of each other, as well as separating out the "animation" of the camera's height, we avoid a lot of headaches and eliminate a lot of potential for messy interwoven bugs.
I hope that helps.
Cinemachine is pretty good, but I suggest people learn on your own quite a bit before using Cinemachine so that you better understand what's going on in the background.
I'm learning unity and c#, and want to make my movement to be camera relative movement instead of world relative movement. How do I do that?
I'm learning unity and c#, my unity version is 2018.3.12f1. I would be happy for help.
just to let know, instead of moving the cam I'm rotating the player.
void Update()
{
float AxisY = Player.transform.eulerAngles.y;
/* Movement starts here */
Vector3 Movement = new Vector3 (Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { //running code
Player.transform.position += Movement * running_speed * Time.deltaTime;
} else {
Player.transform.position += Movement * speed * Time.deltaTime;
}
/*Movement ends here */
/* Rotation controller starts here */
Quaternion target = Quaternion.Euler(Player.transform.eulerAngles.x, Player.transform.eulerAngles.y, Player.transform.eulerAngles.z);
/*if (Player.transform.eulerAngles.x != 0 || Player.transform.eulerAngles.z != 0 || Player.transform.eulerAngles.y != 0) {
Player.transform.rotation = Quaternion.Euler(0,0,0);
}*/
if (Input.GetKey(KeyCode.E))
{
Debug.Log("E got pressed");
//float AxisYPositive = Player.transform.eulerAngles.y;
AxisY = AxisY+1;
Player.transform.rotation = Quaternion.Euler(0, AxisY, 0);
} else if (Input.GetKey(KeyCode.Q))
{
Debug.Log("Q got pressed");
//float AxisYNegetive = Player.transform.eulerAngles.y;
AxisY=AxisY-1;
Player.transform.rotation = Quaternion.Euler(0, AxisY, 0);
}
}
}
The player's movement is world relative, how to make the movement camera relative?
If you want to make the movements relative to the gameObject, call the method Transform.Rotate() on the transform of the gameObject you want to rotate rather than modifying its Quaternion directly. Just make sure the final argument is set to Space.Self.
if (Input.GetKey(KeyCode.E))
{
Debug.Log("E got pressed");
//float AxisYPositive = Player.transform.eulerAngles.y;
AxisY = AxisY+1;
Player.transform.Rotate(Quaternion.Euler(0, AxisY, 0), Space.Self);
}
In general you don't want to directly mess with objects transform.rotation, at least not unless you at least somewhat understand quaternions (I don't!).
I can see a few issues with your code, but the common thread seems to be that you don't really understand how transforms work. Specifically, you might want to look into World/Local space.
The usual way to control a player goes roughly like this:
void DoMovement(Transform player)
{
//If you move first your controls might feel 'drifty', especially at low FPS.
Turn(player);
Move(player);
}
void Turn(Transform player)
{
float yaw = Input.GetAxis("Yaw") * time.deltaTime; //Aka turn left/right
player.Rotate(0, yaw, 0, Space.Self);
// Space.Self is the default value, but I put it here for clarity.
//That means the player will rotate relative to themselves,
//...instead of relative to the world-axis, like in your code.
}
You didn't ask about movement, but as-is your character will always move relative to the world. The below should make it move relative to the camera.
Transform _cameraTransform; //Assumes this is set druing Start()
void Move(Transform player)
{
var forwardMove = _cameraTransform.Forward; //Get whatever direction is 'forward' for camera
forwardMove.Y = 0; //Don't want movement up and down.
forwardMove = forwardMove.normalized; //Normalize sets the 'power' of the vector to 1.
//If you set Y to 0 and don't normalize you'll go slower when camera looks down
//...than when camera is flat along the plane
player.position += forwardMove * Input.GetAxis("Vertical") * time.deltaTime;
//Here you could do the same for strafe/side to side movement.
//Would be same as above, but using the transform.right and Horizontal axis
}
Now, I'm making some assumptions here since you haven't specified what kind of game it is and what kind of controls you want. I'm assuming you have a character running around on a mostly flat plane (no aircraft/spaceship controls), and that the camera is attached to the player. This might not not actually be the case.
In any case I advice you to check out the tutorials, especially the Roll-a-Ball tutorial which I have found is good for beginners to get a grasp on basic players controls that are not just world-relative. The other tutorials, too, are pretty good if you think they're interesting.
Aside from the official Unity tuts a ton of decent to amazing tutorials out there, including video tutorials, so for something like this you could just search for <game type> tutorial and pick whatever seems good to you. While getting started I advice you to avoid the shortest videos, as you will likely benefit greatly from explanation that only fits in longer videos. Of course, that doesn't mean you should pick the longest videos either.
In case someone needs to move an object and don't care about colliders, you can use transform.Translate and assign to his second parameter relativeTo your camera (or any transform) to automatically calculate the translation relative to the object assigned.
I'm trying to rotate a group of objects 90 degrees in unity, so I set all of the objects to the same parent, then rotated the parent, it rotated just fine but it's too fast that I can't see, how can I slow it down? I tried both of codes below and both are the same :\ this code appears in a function that is called by update when the user presses on a button
float totalRotation = 0;
while (Mathf.Abs(totalRotation) < 90){
totalRotation += Time.deltaTime;
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, Time.deltaTime);
}
and this one
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, 90f);
thanks in advance!
Use a factor for the angle, something like
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, Time.deltaTime * 0.5f);
Based on the while I would guess that you actually don't do this in Update. Either way this will not work since update is one frame. You don't want a while to do anything time related like this. Make your while an if instead. That way the rotation will probably be too slow so make the factor bigger. Currently your rotation is instant.
Edit:
Something like this would work:
public bool isRotating = false;
public float speed = 20.0f;
public Vector3 targetRotation;
void Update()
{
if(isRotating)
{
if(Vector3.Distance(transform.eulerAngles, targetRotation) > 1.0f)
{
transform.Rotate(Vector3.up * Time.deltaTime * speed);
}
else
{
transform.eulerAngles = targetRotation;
isRotating = false;
}
}
}
This would be around y only.
This part of the answer doesn't work. Please see the update
Actually you did it in the wrong way. Movements in Unity should be done slowly through updates, not just once. Like ChristophKn, I suggest using Coroutine.
const float speed = 180;
IEnumerator Rotate () {
float rotated = 0;
while (rotated < 90) {
float rotation = speed*Time.fixedDeltaTime;
rotated += rotation;
Parent.transform.RotateAround(temp.transform.position, flag*Vector3.right, rotation);
//wait for the next fixed update to continue.
yield return new WaitForFixedUpdate ();
}
}
To start rotating, call StartCoroutine(Rotate);
EDIT
I have another idea to do this, not using script but animation:
First, add a rotating animation to your parent objects, which rotates the angle you want.
To rotate the group of objects, set them to that parent like you did in your question, then start the animation.
At the end of the animation (you can call your methods at that point using animation events), call SetParent(null,true) for all your objects in the group. This will remove the parent but keep the world position.
Finally, set your parent's rotation to the original value.
Hope this will help.
I am using the Third Person Controller asset by Opsive. It is a fairly complex third person controller that controls animations, damage, movement, inputs, etc etc.
http://opsive.com/assets/ThirdPersonController/documentation.php
I would like to update the ControllerHandler.cs script to make the character move in the direction that the character is facing, regardless of camera orientation. (In the style of old Resident Evil games)
In the original script, the character would move forward in relation to which direction the camera was facing.
I received some advice to change a line in this script, but with the changes, the character moves forward in one fixed direction. (E.G.: When I turn to the right with the D key, and then press W to move forward, the character turns back in the direction he was originally facing and moves in that direction.)
Here is the original part of the script:
#if ENABLE_MULTIPLAYER
if ( isLocalPlayer) {
#endif
if (m_Controller.Movement == RigidbodyCharacterController.MovementType.Combat || m_Controller.Movement == RigidbodyCharacterController.MovementType.Adventure) {
m_LookRotation = m_CameraTransform.rotation;
Here is what someone told me to change it to:
#if ENABLE_MULTIPLAYER
if ( isLocalPlayer) {
#endif
if (m_Controller.Movement == RigidbodyCharacterController.MovementType.Combat || m_Controller.Movement == RigidbodyCharacterController.MovementType.Adventure) {
m_LookRotation = Quaternion.Euler(PlayerInput.GetAxisRaw(Constants.YawInputName), 0, 0);
Unfortunately, this does not have the result that I intended.
Any assistance would be greatly appreciated. Thanks!
Here is a link to the controller script:
https://docs.google.com/document/d/1B4sstqtCqRMCLuHuxEuA9I7tO_3W4aHqEZwr73uFDjY/edit?usp=sharing
I think you want
transform.position += transform.rotation * Vector3.forward;
A complete block of code would look like this...
void Update() {
if (Input.GetKey(KeyCode.W)) {
transform.position += transform.rotation * Vector3.forward * MOVESPEED;
}
}
I have got it working. Take a look at the picture first it will help you set up the player correctly. My player is just two cubes. I added the second cube to give a face to where the player is pointing. I have added those 2 cube to a parent object and I move the parent object with my PlayerMovement.cs.
public float rotSpeed;
public float playerSpeed;
void Update()
{
if (Input.GetKey(KeyCode.RightArrow))
{
transform.Rotate( Vector3.up, Time.deltaTime * rotSpeed);
} else if (Input.GetKey(KeyCode.LeftArrow))
{
transform.Rotate(-Vector3.up, Time.deltaTime * rotSpeed);
}
if (Input.GetKey(KeyCode.UpArrow))
{
transform.Translate(Vector3.forward * Time.deltaTime * playerSpeed);
} else if (Input.GetKey(KeyCode.DownArrow))
{
transform.Translate(-Vector3.forward * Time.deltaTime * playerSpeed);
}
}
I'm trying to make my little small engine in order to make a table Puck slider game like this one :
Then , i'm having problems to move pucks well , cause i'm using velocity normal to make aceleration be opposite to velocity vector:
velocity = ((Vector2) (initMousePosition - Input.mousePosition)).normalized*1.42f*Mathf.Min(1f,((Vector2) (initMousePosition - Input.mousePosition)).magnitude/Screen.height);
acceleration = - velocity.normalized;
...
This are our usefull vars to resolve my problem:
Board.COEFFICIENTE_OF_FRICTION = 0.1f;
public Vector2 position;
public Vector2 velocity;
public Vector2 acceleration;
The code:
private static void Move()
{
foreach(BoardObject obj in objects)
{
if (obj is Puck)
{
auxPuck = (Puck) obj;
if (auxPuck.velocity.magnitude > 0f)
{
auxPuck.acceleration += (-auxPuck.acceleration * 9.8f*auxPuck.mass*Board.COEFFICIENTE_OF_FRICTION*Time.deltaTime);
auxPuck.velocity += auxPuck.acceleration * Time.deltaTime;
auxPuck.position += auxPuck.velocity * Time.deltaTime;
if (VectorsChangeMagnitudeSign(auxPuck.velocity,auxPuck.velocity-auxPuck.acceleration*Time.deltaTime))
{
auxPuck.acceleration = Vector2.zero;
auxPuck.velocity = Vector2.zero;
}
}
}
}
}
Maybe i'll must use acceleration like a float , or starting with another vector that's not the normalized of velocity. My problem comes when velocity got higher magnitude , it seems to have an strange remaining velocity that makes it moves constantly at the end.
Any idea of how must i do this the good way to run it smothly?
Sorry i have found the problem . I told you . I started to use normalized vector , when i throw the puck , the problem comes cause the puck changes direction vs walls , then the accel rest was decreasing the friction force , causing the const velocity.
Sorry for your time , and thanks anyway.