I have a 3D object on Unity and what I want is simply move this object as soon as the user presses the screen. The problem is that it needs to accelerate first and then, when it is reaching the pressed position, it starts decelerating until it totally stops on that point.
I need this to work in a way that if the user moves his finger, the object will recalculate if it needs to accelerate/decelerate based on his current condition.
I tried this example but, it only works for acceleration and it also seems a little confusing. So, I was thinking if someone else have a better and simple idea to solve this. Can physics help with this? If so, how?
My code is in C#.
Using unity rigidbody system and simple calculate. This is using mouse position, you can change it to touch.
public class MyDragMove : MonoBehaviour {
public float speedDelta = 1.0f;
public float decelerate = 1.0f;
private bool startDrag;
private Vector3 prePos;
void Update () {
this.rigidbody.drag = decelerate; //Rigidbody system can set drag also. You can use it and remove this line.
if (Input.GetKeyDown (KeyCode.Mouse0)) {
prePos = Input.mousePosition;
startDrag = true;
}
if(Input.GetKeyUp(KeyCode.Mouse0))
startDrag = false;
if(startDrag)
ForceCalculate();
}
void ForceCalculate()
{
Vector3 curPos = Input.mousePosition;
Vector3 dir = curPos - prePos;
float dist = dir.magnitude;
float v = dist / Time.deltaTime;
this.rigidbody.AddForce (dir.normalized * v * Time.deltaTime * speedDelta);
prePos = curPos;
}
}
or just use SmoothDamp toward last position.
public class MyDragMove : MonoBehaviour {
public float speedDelta = 1.0f;
public float maxSpeed = 5.0f;
public Vector3 v;
private Vector3 prePos;
void Update () {
if(Input.GetKey(KeyCode.Mouse0))
prePos = Input.mousePosition;
TowardTarget ();
}
void TowardTarget()
{
Vector3 targetPos = Camera.main.ScreenToWorldPoint (new Vector3(prePos.x, prePos.y, 10f)); //Assume your camera's z is -10 and cube's z is 0
transform.position = Vector3.SmoothDamp (transform.position, targetPos, ref v, speedDelta, maxSpeed);
}
}
Related
Im making a game where you click and drag with the mouse then release to launch the player. But sometimes the player gets launched in the opposite direction of where it should go. I made a debug output to show you the different values. Here is the output
In that image for example you can see that the Vector2 of force * power is positive on the y axis, but the player launched downwards, and the same happens Viceversa. I think its also worth to note that this happens inconsistantly for some reason. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public GameObject player;
public float power = 10f;
public Rigidbody2D rb;
public float maxSpeed;
public Vector2 minPower;
public Vector2 maxPower;
TragectoryLine tl;
Camera cam;
public Vector2 force;
public Vector3 startPoint;
public Vector3 endPoint;
public Vector3 currentPoint;
public Vector3 startPointMouse;
public bool isPulling = false;
float distance;
private void Start()
{
cam = Camera.main;
tl = GetComponent<TragectoryLine>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
startPointMouse = cam.ScreenToWorldPoint(Input.mousePosition);
startPointMouse.z = 15;
}
if (Input.GetMouseButton(0))
{
startPoint = player.transform.position;
startPoint.z = 15;
isPulling = true;
Vector3 currentPoint = cam.ScreenToWorldPoint(Input.mousePosition);
currentPoint.z = 15;
tl.RenderLine(startPoint, currentPoint);
}
if (Input.GetMouseButtonUp(0))
{
endPoint = cam.ScreenToWorldPoint(Input.mousePosition);
endPoint.z = 15;
isPulling = false;
tl.EndLine();
distance = startPointMouse.magnitude - endPoint.magnitude;
if (distance < 0)
{
distance = -distance;
}
if (distance >= 1)
{
rb.AddForce(force * power, ForceMode2D.Impulse);
}
force = new Vector2(Mathf.Clamp(startPoint.x - endPoint.x, minPower.x, maxPower.x), Mathf.Clamp(startPoint.y - endPoint.y, minPower.y, maxPower.y));
Debug.Log("distance" + distance);
Debug.Log("start" + startPoint);
Debug.Log("end" + endPoint);
Debug.Log("force" +force);
Debug.Log("force * power" + force * power);
}
}
private void FixedUpdate()
{
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxSpeed);
}
}
Here I added the force using rb.AddForce(force * power, ForceMode2D.Impulse); when the force * power value was positive on the y axis. So why did it go to the opposite direction???
This was working perfectly fine before i tried implementing a feature where the player has to move the mouse a certain distance or else it wont launch. I have tried removing it but it doesnt seem to make a difference. I think I changed something in the code that ruined it but I cant figure out what! Please help!
Unity debug ray is every time up to the object that is pointing.
Here one image of the problem.
public class PlayerCamera : MonoBehaviour{
[SerializeField] private Transform transformCamera, transformPlayer;
private Vector3 cameraPosition, playerPosition, cameraRotation;
[Range(0, 6.28f)] public float theta;
[Range(-1.57f, 1.57f)] public float phi;
public float standardCameraDistance = 5, provisionalCameraDistance;
private void Start()
{
transformCamera.GetComponent<Transform>();
transformPlayer.GetComponent<Transform>();
}
void FixedUpdate()
{
playerPosition = transformPlayer.position;
cameraPosition = transformCamera.position;
MoveCamera();
}
void MoveCamera()
{
cameraPosition.y += 1.0f;
RaycastHit rayCastHit;
Physics.Raycast(playerPosition, cameraPosition, out rayCastHit, standardCameraDistance);
Debug.DrawRay(playerPosition, cameraPosition, Color.green); } }
Changing this local Vector3 field in cameraPosition.y += 1.0f; does in no way move the camera. You rather want to assign the result to transformCamera.position
cameraPosition.y += 1.0f;
transformCamera.position = cameraPosition;
or directly without the need of a field
transformCamera.position += Vector3.Up * 1.0f;
A ray consists of a start position and a direction. You are passing in a position (camera) as direction which is not what you want. You rather want the direction vector from playerPosition towards cameraPosition which is
var direction = cameraPosition - playerPosition;
Are you really sure you want your camera moving up 1 Unit every 0.02 seconds? So 50 units per second?
so combined you would probably rather use something like
void MoveCamera()
{
transformCamera.position += Vector3.Up * 1.0f * Time.deltaTime;
var direction = cameraPosition - playerPosition;
var isHit = Physics.Raycast(playerPosition, direction, out var rayCastHit, standardCameraDistance);
Debug.DrawRay(playerPosition, direction.normalized * standardCameraDistance, isHit ? Color.green : Color.red);
if(isHit)
{
// do something with rayCastHit
}
}
Then also note that
transformCamera.GetComponent<Transform>();
transformPlayer.GetComponent<Transform>();
these are not doing anything at all! You either already have the references, then this is useless. Or it will throw exceptions since you can't use methods of something that isn't assigned.
And is there a special reason why you use FixedUpdate which is mainly used for Physics? You probably rather want to execute your code each frame in Update and multiply the movement by Time.deltaTime
I'm working on a third-person run-around game and there's a specific kind of movement I've wanted to achieve but I'm having trouble even imagining how it would work, let alone actually coding it.
Essentially, when holding left or right, I want the player to orbit the camera. Such a camera effect can be seen here. That's exactly what I want to achieve.
Here's my movement and camera code so far. I image I'll need to use the camera's Y rotation to achieve this but my tests haven't worked out. Any input would be appreciated!
Movement:
public int speed = 10;
public int rotationSpeed = 10;
public CharacterController cc;
Vector2 input;
Vector3 moveDir;
Vector3 lookDir;
Vector3 forward;
Transform camTransform;
public Transform model;
void Start () {
cc = GetComponent<CharacterController>();
camTransform = Camera.main.transform;
}
void GetInput() {
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
}
void CalculateForward()
{
}
void Move()
{
moveDir.x = input.x;
moveDir.z = input.y;
if(moveDir.magnitude > 1)
{
moveDir.Normalize();
}
Vector3 rotatedDir = Camera.main.transform.TransformDirection(moveDir);
rotatedDir = new Vector3(rotatedDir.x, 0, rotatedDir.z);
rotatedDir = rotatedDir.normalized * moveDir.magnitude;
cc.Move(rotatedDir * speed * Time.deltaTime);
}
void ApplyGravity()
{
}
void FaceDir()
{
if (moveDir != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(
transform.rotation,
Quaternion.LookRotation(-moveDir),
Time.deltaTime * rotationSpeed
);
}
}
private void FixedUpdate()
{
GetInput();
//ApplyGravity();
Move();
FaceDir();
ApplyGravity();
}
}
Camera:
[SerializeField]
private float distanceAway;
[SerializeField]
private float distanceUp;
[SerializeField]
private float smooth;
[SerializeField]
private Transform follow;
private Vector3 targetPosition;
private void Start()
{
follow = GameObject.FindWithTag("Player").transform;
}
private void LateUpdate()
{
targetPosition = follow.position + follow.up * distanceUp - follow.forward * distanceAway;
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * smooth);
transform.LookAt(follow);
}
Movement around a circle is always in a direction tangential to the circle, so with each update you want to:
Set the Y rotation of the player to be looking directly at the Y axis of the camera
rotate the player 90 degrees left or right (depending on the direction you want to go)
move the player forward a distance that will depend on the speed with which you would like them to move
Remember it's impossible to traverse a circle perfectly because there are infinitely many points on a circle but this approach will approximate it well enough.
I mean that when the character is walking or running and getting to the clicked point he change the state in the animator from walk/run to idle so it looks like he walk then stop there is no animation between the walk/run and the start/stop.
I have 3 states in the animator. HumanoidWalk, HumanoidRun, HumanoidIdle.
I need something like fading.
For example if in the line:
_animator.CrossFade("Walk", 0);
I will change the 0 to 1 so when he start "Walk" it will be a bit slowly to walking speed. But in "Idle" if i will change it to 1 it will be something else not fading until he stop.
In other words i want to add fading effects when character start/stop walking/running and also when i click/double click and he switch between Walk and Run. Make some fade effects so it will not switch between the states so fast.
using UnityEngine;
using System.Collections;
public class ClickToMove : MonoBehaviour
{
public int speed = 5; // Determines how quickly object moves towards position
public float rotationSpeed = 5f;
private Vector3 targetPosition;
private Animator _animator;
private Vector3 destination;
private Quaternion targetRotation;
public float clickDelta = 0.35f; // Max between two click to be considered a double click
private bool click = false;
private float clickTime;
void Start()
{
_animator = GetComponent<Animator>();
_animator.CrossFade("Idle", 0);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if (click && Time.time <= (clickTime + clickDelta))
{
_animator.CrossFade("Run", 0);
click = false;
}
else
{
click = true;
clickTime = Time.time;
}
_animator.CrossFade("Walk", 0);
Plane playerPlane = new Plane(Vector3.up, transform.position);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float hitdist = 0.0f;
if (playerPlane.Raycast(ray, out hitdist))
{
Vector3 targetPoint = ray.GetPoint(hitdist);
targetPosition = ray.GetPoint(hitdist);
targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
destination = targetPosition;
}
}
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed);
if ((transform.position - destination).magnitude < 0.7f)
{
_animator.CrossFade("Idle", 0);
}
}
}
If you increase your transform duration value a little (like 0.25), then you can get a smooth transition between states. Also uncheck "fixed duration".
I'm working on my latest project, which is a action sidescroller, primarily 2D but using a perspective camera for 3D effect. I've gotten the basics working - design, etc, and now fixing the camera so that it follows correctly. However, I'd like to keep the player on the left side of the viewport - not actually modifying the player's position, but the camera following an offset. I have it partially worked out, but it broke the moment I started playing around with other alternative resolutions - say, from web standard, standalone, to mobile devices. This is what I want to do:
This is my code attempt thus far. The camera itself works perfectly for basic up/down right scrolling, but when the resolution changes the player can vanish:
public Transform TrackTarget;
public float dampTime = 0.15f;
public bool Ready = false;
private Vector3 velocity = Vector3.zero;
private Vector3 TargetOffset = Vector3.zero;
void Start()
{
TargetOffset = new Vector3(16,0,0);
Vector3 point = TrackTarget.position + TargetOffset;
Vector3 Destination = new Vector3(point.x, transform.position.y, transform.position.x);
transform.position = Destination;
}
void FixedUpdate () {
if (Ready) {
if (TrackTarget)
{
Vector3 point = TrackTarget.position + TargetOffset;
Vector3 Destination = new Vector3(point.x, transform.position.y, transform.position.x);
transform.position = Vector3.SmoothDamp(transform.position, Destination, ref velocity, dampTime);
}
}
}
void LateUpdate()
{
if (Ready) {
Vector3 CameraPosition = transform.position;
CameraPosition.z = -30.00f;
transform.position = CameraPosition;
}
}
This may be helpful.
public class CameraController : MonoBehaviour {
public Transform target;
public float distance = 5;
public float heigt = 1;
public float width = 6;
public float damping = 5;
void Start()
{
Vector3 unicornPos = camera.WorldToViewportPoint(target.position);
width = camera.ViewportToWorldPoint(new Vector3(1, 1, camera.nearClipPlane)).x/3;
}
void LateUpdate () {
Vector3 wantedPosition = target.TransformPoint (width, heigt, -distance);
transform.position = Vector3.Lerp (transform.position, wantedPosition, Time.deltaTime * damping);
}
}