I made a 3d game with Unity and C#. I don't know how to make a object (player) control to left and right with swipe on mobile screen.
That's my C# code:
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Rigidbody rb;
public float forwardForce = 300f;
public float sidewaysForce = 200f;
// Update is called once per frame
void FixedUpdate()
{
rb.AddForce(0, 0, forwardForce * Time.deltaTime);
if (Input.GetAxis("Horizontal") > 0.1)
{
rb.AddForce(sidewaysForce * Time.deltaTime, 0, 0);
}
if (Input.GetAxis("Horizontal") < -0.1)
{
rb.AddForce(-sidewaysForce * Time.deltaTime, 0, 0);
}
}
}
You should have a look on the Mobile Touch Tutorials, the Mobile Touch Manual and Input.GetTouch. In short: You have to get the touch, store the initial position and than compare it to current positions.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Rigidbody rb;
public float forwardForce = 300f;
public float sidewaysForce = 200f;
private Vector2 initialPosition;
// Update is called once per frame
void Update()
{
// are you sure that you want to become faster and faster?
rb.AddForce(0, 0, forwardForce * Time.deltaTime);
if(Input.touchCount == 1)
{
touch = Input.GetTouch(0);
if(touch.phase == TouchPhase.Began)
{
initialPosition = touch.position;
}
else if(touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
{
// get the moved direction compared to the initial touch position
var direction = touch.position - initialPosition;
// get the signed x direction
// if(direction.x >= 0) 1 else -1
var signedDirection = Mathf.Sign(direction.x);
// are you sure you want to become faster over time?
rb.AddForce(sidewaysForce * signedDirection * Time.deltaTime, 0, 0);
}
}
}
}
Note: This always compares to the initial touch position => you don't go into the other direction until you
Move the touch over the initial position in the other direction
And since you use AddForce wait the same amount of time as you added force in the other direction
Maybe you should rather only compare to the previos touch position instead of comparing to the initial one:
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Rigidbody rb;
public float forwardForce = 300f;
public float sidewaysForce = 200f;
private Vector2 lastPosition;
// Update is called once per frame
void Update()
{
// are you sure that you want to become faster and faster?
rb.AddForce(0, 0, forwardForce * Time.deltaTime);
if(Input.touchCount == 1)
{
touch = Input.GetTouch(0);
if(touch.phase == TouchPhase.Began)
{
lastPosition = touch.position;
}
if(touch.phase == TouchPhase.Moved)
{
// get the moved direction compared to the initial touch position
var direction = touch.position - lastPosition ;
// get the signed x direction
// if(direction.x >= 0) 1 else -1
var signedDirection = Mathf.Sign(direction.x);
// are you sure you want to become faster over time?
rb.AddForce(sidewaysForce * signedDirection * Time.deltaTime, 0, 0);
lastPosition = touch.position;
}
}
}
}
Now you still have to swipe the same amount of time ... it doesn't take the swiped distance into account. So Maybe instead you should use that:
// get the moved direction compared to the initial touch position
var direction = touch.position - lastPosition ;
// are you sure you want to become faster over time?
rb.AddForce(sidewaysForce * direction.x * Time.deltaTime, 0, 0);
so the more you swipe the more force gets added.
Related
Player movement is working (at least somewhat) now, however one issue remains, and that's the insane numbers the y velocity of the Rigidbody2D on the player. Since the isGrounded check I plan to add will use velocity for the sake of stability, this needs to be fixed.
It confuses me, considering the velocity is 0 normally, but whenever moving left or right it changes to said high numbers.
Movement code:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerM : MonoBehaviour
{
private PControls control;
Rigidbody2D rb;
SpriteRenderer sp;
Transform tr;
public float speed = 0f;
public float speedC;
public float heightSpeed = 5f;
public float heightC;
public bool grounded;
void Start()
{
control = new PControls();
control.Enable();
rb = GetComponent<Rigidbody2D>();
sp = GetComponent<SpriteRenderer>();
tr = GetComponent<Transform>();
}
// Update is called once per frame
void FixedUpdate()
{
RaycastHit2D hit = Physics2D.Raycast(rb.position, -Vector2.up);
Color color = new Color(0, 0, 1.0f);
Debug.DrawRay(transform.position, Vector2.down);
speedC = rb.velocity.magnitude;
var pos = control.Movement.Move.ReadValue<float>();
float GetVerticalSpeed() => rb.velocity.y;
if(pos == -1)
{
sp.flipX = true;
}
else if(pos == 1)
{
sp.flipX = false;
}
if((pos == 0) && (speed > 0.1f))
{
speed -= 3f * Time.deltaTime;
}
else if(speed < 1.4f)
{
speed += Mathf.Abs(pos) * 8 * Time.deltaTime;
}
if(speedC < 7f)
{
rb.AddForce(new Vector3((pos * 5), 0f) * speed * Time.deltaTime, ForceMode2D.Impulse);
}
var jump = control.Movement.Jump.ReadValue<float>();
Debug.Log(GetVerticalSpeed());
Vector3 v = rb.velocity;
v.y = 10;
if(jump == 1)
{
rb.velocity = v;
}
}
}
Seems the main issue was fixed by converting the height velocity to an integer using System.Math.Round():
float vel = rb.velocity.y;
heightC = (int)System.Math.Round(vel);
Not sure it's the best solution, but it's something..
I have a fish which is swimming across the screen. When it gets within 10% of the edge of the screen, I want it to turn begin turning around until it has completely reversed and is now swimming in the opposite direction. This should be more gradual like a fish would swim. I don't want it to rotate on its own axis.
I'm not having any luck getting it to turn around. It only turns partially.
Update
Here's the fish
public class FishSwim : MonoBehaviour
{
public enum Direction { LeftToRight, RightToLeft };
public Direction moveDirection;
[SerializeField]
private float speedMin = 0.5f;
[SerializeField]
private float speedMax = 1f;
[SerializeField]
private bool useOnlySpeedMax = false;
private float speed;
[HideInInspector]
public float removeBeyond;
private void Start()
{
var dist = (transform.position - Camera.main.transform.position).z;
if (moveDirection == Direction.RightToLeft)
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
else
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x + FindObjectOfType<SkinnedMeshRenderer>().bounds.size.x;
}
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
if (moveDirection == Direction.RightToLeft)
{
speed = -speed;
}
}
// Update is called once per frame
void Update()
{
float realSpeed = speed * Time.deltaTime;
transform.position += Vector3.right * realSpeed;
if (moveDirection == Direction.RightToLeft && transform.position.x < -Mathf.Abs(removeBeyond))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), Time.deltaTime * 40f);
moveDirection = Direction.LeftToRight;
}
else if (moveDirection == Direction.LeftToRight && transform.position.x > Mathf.Abs(removeBeyond))
{
}
}
}
I would use Coroutines to do this. Explanation in comments:
bool isTurning = false;
[SerializeField] float turnPeriod = 0.5f; // how long it takes to turn, smaller=faster
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
// Get rid of negative speed
}
void Update()
{
float realDistance = speed * Time.deltaTime;
transform.position += transform.right * realDistance;
// Surely there is a better way than calculating
// Mathf.Abs(removeBeyond) every frame... but that is off-topic
if (!isTurning && (
(moveDirection == Direction.RightToLeft
&& transform.position.x < -Mathf.Abs(removeBeyond)
) || (moveDirection == Direction.LeftToRight
&& transform.position.x > Mathf.Abs(removeBeyond)
) ) )
{
// If we aren't already turning and we should be, start turning:
StartCoroutine(Turn());
}
}
IEnumerator Turn()
{
isTurning = true;
Vector3 startForward = transform.forward;
// find end speed and direction
Direction endDirection = moveDirection == Direction.RightToLeft ?
Direction.LeftToRight:Direction.RightToLeft;
// keep track of how much of our turning time has elapsed
float elapsedTimePortion = Time.deltaTime/turnPeriod;
// turn until you've spent enough time turning
while (elapsedTimePortion < 1f)
{
// by whatever portion we've spent time turning, turn starting forward
// 180 degrees around up axis
float angle = 180f * elapsedTimePortion;
Vector3 newForward = Quaternion.AngleAxis(angle, Vector3.up) * startForward;
transform.rotation = Quaternion.LookRotation(newForward);
yield return null;
// next frame - update how long we've been turning
float newElapsedTimePortion = elapsedTimePortion + Time.deltaTime/turnPeriod;
if (newElapsedTimePortion >= 0.5f && elapsedTimePortion < 0.5f)
{
// If we've just passed 50% of the turn,
// make fish move opposite direction
moveDirection = endDirection;
}
elapsedTimePortion = newElapsedTimePortion;
}
// we're done turning, just set the rotation to the end rotation.
isTurning = false;
transform.rotation = Quaternion.LookRotation(-startForward);
// Does removeBeyond need to be updated here? There are a few lines in Start()
// which would suggest that.
}
Im trying to make a 3d game and i want to make the camera move by moving your finger on the screen, its an android game
i searched script on google and i found some but they didnt work... now im using a script but it doesnt behave too good.... if you move your finger from left to right the camera will move as it should but if you keep your finger on the screen and then move the finger from right to left it will keep moving as before for a bit and sometimes it just freaks out ....
here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveTouch : MonoBehaviour
{
private Touch initTouch = new Touch();
public Camera cam;
private float rotX = 0.0f;
private float rotY = 0.0f;
private Vector3 origRot;
public float rotSpeed = 0.5f;
public float dir = -1;
private Touch touch;
// Start is called before the first frame update
void Start()
{
origRot = cam.transform.eulerAngles;
rotX = origRot.x;
rotY = origRot.y;
}
// Update is called once per frame
void FixedUpdate()
{
foreach (Touch touch in Input.touches)
{
if (touch.phase == TouchPhase.Began)
{
initTouch = touch;
}
else if (touch.phase == TouchPhase.Moved)
{
//swiping
float deltaX = initTouch.position.x - touch.position.x;
float deltaY = initTouch.position.y - touch.position.y;
if (deltaX != 0)
{
rotX -= deltaY * Time.deltaTime * rotSpeed;
}
if (deltaY != 0)
{
rotY -= deltaX * Time.deltaTime * rotSpeed * dir;
}
Mathf.Clamp(rotX, -45f, 45f);
cam.transform.eulerAngles = new Vector3(rotX, rotY, 0.0f);
}
else if (touch.phase == TouchPhase.Ended)
{
initTouch = new Touch();
}
}
}
}
All your routine is in FixedUpdate().
But Unity docs state that Input.touches:
Returns list of objects representing status of all touches during last frame. (Read Only) (Allocates temporary variables).
All calls to Input.whateverinputyouareusing should be in Update() that is called every frame. Instead, FixedUpdate() is called at regular intervals, sometimes more than once in a single frame (and sometimes it’s not called at all during a frame), and it’s usage is specifically for Physics calculations.
In my game, I want to clamp the player's camera so it can't do frontflips or backflips. I want to clamp the x-axis between -75 and 50 but it just won't work.
Every time I add a clamp such as the one in my code, the camera doesn't want to move it's rotation from 0,0,0 farther than Input.GetAxisRaw says the mouse is moving.
I've tried using if statements as manual clamps too but it either stays at 0,0,0 constantly or -75,0,0 constantly if I switch the polarity.
I've tried replacing the camera in case it was something related to its settings but nothing changes.
I didn't want to post this because it shouldn't be this hard but I've spent multiple days on this and I'm out of options; any and all ideas are much appreciated.
I am using Visual Studio as my editor.
using UnityEngine;
public class PlayerMove : MonoBehaviour {
Rigidbody rb;
public Camera cam;
public Transform camTrans;
Vector3 movement;
Vector3 rotation;
public float sensitivityX;
public float sensitivityY;
public float playerSpeed;
public float jumpForce;
float forward;
float sideways;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate ()
{
forward = Input.GetAxisRaw("Vertical");
sideways = Input.GetAxisRaw("Horizontal");
movement = new Vector3 (forward, 0, -sideways).normalized;
float _xRot = Input.GetAxisRaw("Mouse Y");
float _yRot = Input.GetAxisRaw("Mouse X");
rotation = new Vector3(0f, _yRot, 0f) * sensitivityX;
float _jump = Input.GetAxisRaw("Jump");
if (movement != Vector3.zero)
{
MovePlayer();
}
if (rotation != Vector3.zero && Input.GetAxisRaw("Fire2") != 0 || _xRot != 0 && Input.GetAxisRaw("Fire2") != 0)
{
Rotate(-_xRot);
}
if (_jump != 0f)
{
Jump();
}
}
void MovePlayer()
{
float _playerSpeed;
_playerSpeed = playerSpeed * 0.1f;
transform.Translate(movement * _playerSpeed * Time.fixedDeltaTime, Space.Self);
}
void Jump()
{
if (IsGrounded())
{
rb.AddForce(new Vector3(0, 1 * jumpForce, 0), ForceMode.Impulse);
}
}
void Rotate(float _camRot)
{
camTrans.Rotate(new Vector3(_camRot * sensitivityY, 0, 0));
float _camPosX = camTrans.rotation.x;
Mathf.Clamp(_camPosX, -75, 50);
camTrans.rotation = Quaternion.Euler(new Vector3(_camPosX, 0, 0));
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation * sensitivityX));
}
bool IsGrounded()
{
RaycastHit hit;
return Physics.Raycast(transform.position, Vector3.down, out hit, 1.001f);
}
}
Input.GetAxisRaw("Mouse Y"); returns the number of units the mouse moved (See Unity Documentation - Input.GetAxisRaw).
When you move your mouse, the script works, but because void Rotate() called each frame in Update() function, after some frames Input.GetAxisRaw("Mouse Y"); returns 0 and then _camRot = 0.
So, camTrans.rotation = Quaternion.Euler(new Vector3(0, 0, 0)); and because Update() called multiple times per second, We think camera stays at 0,0,0 constantly.
To clamp the rotation, you can change your code to this:
public class PlayerMove : MonoBehaviour
{
...
private float rotationX;
...
void Rotate(float _camRot)
{
rotationX += _camRot * sensitivityY;
rotationX = Mathf.Clamp(rotationX, -75, 50);
camTrans.localEulerAngles = new Vector3(rotationX, camTrans.localEulerAngles.y, camTrans.localEulerAngles.z);
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation * sensitivityX));
}
}
I hope it helps you.
What I need is that an NPC that is moving in a path by script, stops in front of the player if the player is in the way.
It's a top-down 2d game rpg with grid movement.
The NPC is moving 4 cells down, 2 cells left, 4 cells up and 2 cells right all the time. I need that if the player is in the way, it stops in front and continue when the player leaves the grid.
I don't want to use a collider on the player because then I need a rigitbody2d and with that, my movement script is not working.
Here is the TileMovementController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class TileMovementController : MonoBehaviour {
public enum Direction { Left, Right, Up, Down }; // Direction of movement
protected Vector3 newPosition; // For movement
protected virtual Vector3 move(Direction dir, int steps) {
if (dir == Direction.Left && transform.position == newPosition)
newPosition += new Vector3(steps * (-1), 0, 0);
else if (dir == Direction.Right && transform.position == newPosition)
newPosition += new Vector3(steps, 0, 0);
else if (dir == Direction.Up && transform.position == newPosition)
newPosition += new Vector3(0, steps, 0);
else if (dir == Direction.Down && transform.position == newPosition)
newPosition += new Vector3(0, steps * (-1), 0);
return newPosition;
}
}
The PlayerMovementController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementController : TileMovementController {
public float moveSpeed = 2.0f;
void Start () {
newPosition = transform.position; // Take the initial position
}
private void FixedUpdate() {
// RayCasts for collisions, ignoring layer 2 (Ignore Raycast)
if (Input.GetKey(KeyCode.A) && Physics2D.Raycast(transform.position, Vector2.left, 1, ~(1 << 2)).collider == null) { // Left
newPosition = move(Direction.Left, 1);
}
if (Input.GetKey(KeyCode.D) && Physics2D.Raycast(transform.position, Vector2.right, 1, ~(1 << 2)).collider == null) { // Right
newPosition = move(Direction.Right, 1);
}
if (Input.GetKey(KeyCode.W) && Physics2D.Raycast(transform.position, Vector2.up, 1, ~(1 << 2)).collider == null) { // Up
newPosition = move(Direction.Up, 1);
}
if (Input.GetKey(KeyCode.S) && Physics2D.Raycast(transform.position, Vector2.down, 1, ~(1 << 2)).collider == null) { // Down
newPosition = move(Direction.Down, 1);
}
transform.position = Vector3.MoveTowards(transform.position, newPosition, Time.deltaTime * moveSpeed); // Move there
}
}
And the NpcMovementController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NpcMovementController : TileMovementController {
// Defines the direction and number of steps in that direction has to move
[System.Serializable]
public struct MoveStep {
public Direction direction;
public int steps; // Each step is a grid square. 1 step = 1 unit in the grid position
public MoveStep(Direction direction, int steps) {
this.direction = direction;
this.steps = steps;
}
}
public List<MoveStep> path = new List<MoveStep>();
public float moveSpeed = 2.0f;
private int nextStepIndex = 0;
private bool waiting = false;
void Start () {
newPosition = transform.position; // Take the initial position
}
private void FixedUpdate() {
if (path.Count > 0) {
if (Vector3.Distance(transform.position, newPosition) < 0.05f && !waiting) {
transform.position = newPosition; // Adjust the position to be exactly what it should be
waiting = true;
StartCoroutine("wait", 2f);
} else {
if (Vector3.Distance(transform.position, newPosition) > 0.05f)
transform.position = Vector3.MoveTowards(transform.position, newPosition, Time.deltaTime * moveSpeed);
}
}
}
IEnumerator wait(float seconds) {
yield return new WaitForSecondsRealtime(seconds);
newPosition = move(path[nextStepIndex].direction, path[nextStepIndex].steps);
if (nextStepIndex == path.Count - 1)
nextStepIndex = 0;
else
nextStepIndex++;
waiting = false;
}
}
In the NPCMovementController I tried to do a raycast to player, but without the collider/rigitbody it does not work obviously. If I attach the collider/rigitbody it detects the player but I cannot move.
I did once something similar and I solved by checking if the position of the player is inside the grid squares which represent the path of the npc. For example.
Let's imagine that the npc moves like this:
(0,0) -> (0,1) -> (0,2) -> (0,3) -> (1,3) -> (2,3) -> (2,2) -> (2,1) -> (2,0) -> (1,0) -> (0,0) loop
So for my understanding, if the npc is in (0,0), it will detect the player in (0,1), (0,2) and (0,3). So you can check this three positions in your grid and compare them with the current position of the player, if there is a match, the player is in the path.
Now besides that, it is possible to move your player using the rigidbody (so you can keep with your raycast approach). You can use the velocity of the rigidbody to move the npc in one direction or another.
//moveHorizontal and moveVertical can have +1 or -1 values depending on the inputs of the player:
Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
rigidbody.velocity = movement * speed;