I am making a 2D point-and-click game where i want the player to move towards clicked objects. This is my code for moving the player towards a door:
using UnityEngine;
using System.Collections;
public class MoveOnClick : MonoBehaviour {
public GameObject door;
public GameObject player;
public float speed;
public Vector3 target;
void Update () {
if (Input.GetMouseButtonDown (0)) {
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector3.zero);
if (hit.collider != null) {
player.transform.position = Vector3.MoveTowards(player.transform.position, target, speed * Time.deltaTime);
}
}
}
}
The problem is that the player only moves one pixel per click. I want the player to move all the way to the door if the door is clicked.
That should work:
void Update () {
if (Input.GetMouseButtonDown (0)) {
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector3.zero);
target = hit.transform.position;
}
if (hit.collider != null) {
player.transform.position = Vector3.MoveTowards(player.transform.position, target, speed * Time.deltaTime);
}
}
Related
I'm trying to make a basic FPS game in Unity and I'm having an issue where the projectiles I shoot won't instantiate in the right place. From what I can tell, the projectile instantiates in roughly the same position relative to the player regardless of where I look (that position being a little to the left of the starting angle of the player).
Here's a 20 second video demonstration of what I'm talking about.
https://youtu.be/WLVuqUtMqd0
Even when I'm facing the exact opposite direction of where the projectile usually instantiates it still spawns in the same place, which means the projectile ends up spawning behind the player and hitting the player.
I tried using Debug.DrawRay() to see if maybe the firepoint itself is wrong (which would be shown by the ray starting somewhere other than the gun barrel). However it seems like the starting point of the ray is correct every time.
I'm not sure if this is related to the issue above, but I have noticed that the ray direction is wrong if I'm looking a little below the horizon. It seems like the projectile's direction is correct regardless though.
Ray directions when looking slightly above the horizon vs. lower
Here's my code for intantiating/shooting the projectile. I think the most relevant parts are probably shootProjectile() and instantiateProjectile(). This script is attached to the player character.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting : MonoBehaviour
{
public Camera cam;
public GameObject projectile;
public Transform firePoint;//firePoint is set to the end of the barrel.
public float projectileSpeed = 40;
//private var ray;
private Vector3 destination;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//var ray = cam.ViewportPointToRay(new Vector3(0.5f,0.5f,0));
//Debug.DrawRay(ray.origin, ray.direction);
if(Input.GetButtonDown("Fire1")) {
//player is shooting
ShootProjectile();
}
}
void ShootProjectile() {
Ray ray1 = cam.ScreenPointToRay(Input.mousePosition);
//Debug.Log(ray.direction);
RaycastHit hit;
if(Physics.Raycast(ray1, out hit))//checking whether the player is going to hit something
{
destination = hit.point;
}
else {
destination = ray1.GetPoint(1000);
}
Debug.DrawRay(firePoint.position, destination, Color.white, 10f);
InstantiateProjectile(firePoint);
}
void InstantiateProjectile(Transform firePoint) {
var projectileObj = Instantiate (projectile, firePoint.position, Quaternion.identity) as GameObject;//projectile is instantiated
projectileObj.GetComponent<Rigidbody>().velocity = (destination - firePoint.position).normalized * projectileSpeed;//projectile is set in motion
}
}
Here's the location of firePoint.
firePoint (i.e. where the projectile should instantiate)
I would appreciate any help on this, as I've been trying to fix it (on and off) for several days, and really have no idea what the problem is.
Edit: Here's my player movement script(s) as well. The first one is PlayerController.cs, and it converts the player's inputs into the appropriate movement vectors and camera rotation. It then calls methods from PlayerMotor.cs, which actually performs the movements.
PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
public class PlayerController : MonoBehaviour
{
[SerializeField]
public float speed = 10f;
[SerializeField]
private float lookSens = 3f;
private PlayerMotor motor;
void Start() {
motor = GetComponent<PlayerMotor>();
//Debug.Log("PlayerControllerStart");
Cursor.lockState = CursorLockMode.Locked;
}
void Update() {
//Debug.Log("PlayerControllerUpdate");
//calculate movement velocity as 3D vector.
float xMov = Input.GetAxisRaw("Horizontal");
float zMov = Input.GetAxisRaw("Vertical");
Vector3 movHorizontal = transform.right * xMov;
Vector3 movVertical = transform.forward * zMov;
Vector3 velocity = (movHorizontal + movVertical).normalized * speed;
motor.move(velocity);
//speed*=(float)1.15;
//rotation
float yRot = Input.GetAxisRaw("Mouse X");
Vector3 rotation = new Vector3 (0f, yRot, 0f) * lookSens;
motor.rotate(rotation);
float xRot = Input.GetAxisRaw("Mouse Y");
Vector3 cameraRotation = new Vector3 (xRot, 0f, 0f) * lookSens;
motor.rotateCamera(cameraRotation);
if (Input.GetKeyDown(KeyCode.Space) == true && motor.isGrounded()) {
motor.canJump=true;
}
if (Input.GetKey(KeyCode.W) == true) {
motor.accel=true;
}
else{
motor.accel=false;
}
}
}
PlayerMotor:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMotor : MonoBehaviour
{
[SerializeField]
private Camera cam;
private Vector3 velocity = Vector3.zero;
private Vector3 rotation = Vector3.zero;
private Vector3 cameraRotation = Vector3.zero;
private Vector3 jumpVector = new Vector3 (0f, 5f, 0f);
private PlayerController pc;
private Rigidbody rb;
public bool canJump;
public bool accel;
public float acceleration;
int jumpCount;
void Start() {
rb = GetComponent<Rigidbody>();
pc = GetComponent<PlayerController>();
canJump=false;
jumpCount=0;
accel=false;
//acceleration = 1.0;
//distToGround = collider.bounds.extents.y;
//Debug.Log("PlayerMotorStart");
}
//sets velocity to a given movement vector.
public void move(Vector3 _velocity) {
velocity = _velocity;
}
public void rotate(Vector3 _rotation) {
rotation = _rotation;
}
public void rotateCamera(Vector3 _cameraRotation) {
cameraRotation = _cameraRotation;
}
public bool isGrounded() {
return Physics.Raycast(transform.position, -Vector3.up, (float)2.5);
}
public void jump() {
rb.AddForce(transform.up * 250f);
//Debug.Log("Jump"+jumpCount);
jumpCount++;
canJump=false;
}
void FixedUpdate() {
performMovement();
performRotation();
if (canJump) {
jump();
}
//Debug.Log("PlayerMotorUpdate");
if(accel&&pc.speed<20f&&isGrounded())
{
//Debug.Log("W Pressed");
pc.speed*=(float)1.005;
}
else if(!accel) {
pc.speed=7f;
}
}
void performMovement() {
if(velocity != Vector3.zero) {
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
//Debug.Log("Movement");
}
}
void performRotation() {
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
if(cam != null) {
cam.transform.Rotate(-cameraRotation);
}
}
}
Here are some pictures of my projectile prefab as well.
To solve one first confusion: The method Debug.DrawRay takes as paramters
a start position
a ray direction(!)
You are passing in another position which is not what you want.
This might only work "accidentally" as long as you stand on 0,0,0 so maybe it wasn't that notable but the debt ray definitely points into a wrong direction.
Rather do e.g.
Debug.DrawRay(firePoint.position, destination - firePoint.position, Color.white, 10f);
Or instead rather use Debug.DrawLine which rather actually takes
start position
end position
Then you don't need to calculate the direction first
Debug.DrawLine(firePoint.position, destination, Color.white, 10f);
Then I suspect the following is happening: The spawned projectileObj actually is placed correctly but it is the visuals inside that projectile prefab that have a local offset against the parent prefab root.
Since you always spawn the projectile in world rotation Quaternion.identity that offset stays the same no matter where you look.
I guess a simple fix would already be to also apply the same rotation like
var projectileObj = Instantiate (projectile, firePoint.position, firePoint.rotation);
so I have an issue about movement click on unity it's about when I click of course the player will be moving to the position I clicked but, I don't want if I clicked on the wall and another button the player will be on the last mouse I clicked, I already add some collider but the player just bypass the collider so collider doesn't have any effect
the explanation
and this the script
[Header("Tweak")]
[SerializeField] Transform target;
Vector2 targetPos;
public float speed = 5f;
// Start is called before the first frame update
void Start()
{
targetPos = transform.position;
}
// Update is called once per frame
void Update()
{
Move();
}
public void Move()
{
if (Input.GetMouseButtonDown(0))
{
targetPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
if ((targetPos.x >= transform.position.x))
{
transform.localScale = new Vector2(.5f, .5f);
}
else
{
transform.localScale = new Vector2(-.5f, .5f);
}
target.position = targetPos;
}
if ((Vector2)transform.position != targetPos)
{
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
}
}
You can do this using Raycast
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
if (hitInfo.collider.gameObject.tag == "Wall")
{
Debug.Log("tag");
//You can do what ever you want here.
}
}
}
I am integrating my game to VR and now i wanted to change my controlling. In the script given bellow when the player press the mouse button the gun fires the bullet but now in VR i don't want my game to be controlled by any controller. what i want is when player moves his head and the focus point comes to the Enemy it starts shooting without pressing any button(I mean auto shoot). what should i do??
grausing System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunScript : MonoBehaviour {
public float damage = 10f;
public float range = 100f;
public float fireRate = 15f;
public float impactForce = 30f;
public Camera fpsCam;
public GameObject impactEffect;
private float nextTimeToFire = 0f;
// Update is called once per frame
void Update () {
if (Input.GetButton("Fire1") && Time.time >= nextTimeToFire)
{
nextTimeToFire = Time.time + 1f / fireRate;
Shoot();
}
}
void Shoot()
{
RaycastHit hit;
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Target target = hit.transform.GetComponent<Target>();
if (target != null)
{
target.TakeDamage(damage);
}
if(hit.rigidbody != null)
{
hit.rigidbody.AddForce(-hit.normal * impactForce);
}
GameObject ImpactEffectGO = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(ImpactEffectGO, 2f);
}
}
}
Since you already have an existing shooting mechanic, simply changing up the Update() should do what you want
void Update () {
if (Time.time >= nextTimeToFire)
{
nextTimeToFire = Time.time + 1f / fireRate;
Shoot();
}
}
I have a FPSController attached to it: a RigidBody , Animator , NavMeshAgentand two scripts: PlayerController and AgentController.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float speed = 10.0f;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
float translatioin = Input.GetAxis("Vertical") * speed;
float straffe = Input.GetAxis("Horizontal") * speed;
translatioin *= Time.deltaTime;
straffe *= Time.deltaTime;
transform.Translate(straffe, 0, translatioin);
if (Input.GetKeyDown("escape"))
Cursor.lockState = CursorLockMode.None;
}
}
And
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentController : MonoBehaviour
{
public Camera camera;
public NavMeshAgent agent;
public bool agentControl = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
if (Input.GetMouseButtonDown(0) && agentControl == true)
{
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
agent.SetDestination(hit.point);
}
}
}
}
With the PlayerController I can move around using the keys WSAD and with the AgentController I can click on some point on the terrain and the Agent will walk over there.
When I click for example on one of the big cubes the player is walks over it and stop near it. But then when I'm using the keys WSAD to move back away from the cube the player will keep moving automaticaly back to the cube.
Like a magnet that make the player keep moving to the cube to the same point I clicked on before.
On the FPSCamera I have a script: Just to use the mouse look around.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class camMouseLook : MonoBehaviour
{
Vector2 mouseLook;
Vector2 smoothV;
public float sensitivity = 5.0f;
public float smoothing = 2.0f;
GameObject character;
// Use this for initialization
void Start ()
{
character = this.transform.parent.gameObject;
}
// Update is called once per frame
void Update ()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
var md = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
md = Vector2.Scale(md, new Vector2(sensitivity * smoothing, sensitivity * smoothing));
smoothV.x = Mathf.Lerp(smoothV.x, md.x, 1f / smoothing);
smoothV.y = Mathf.Lerp(smoothV.y, md.y, 1f / smoothing);
mouseLook += smoothV;
mouseLook.y = Mathf.Clamp(mouseLook.y, -90f, 90f);
transform.localRotation = Quaternion.AngleAxis(-mouseLook.y, Vector3.right);
character.transform.localRotation = Quaternion.AngleAxis(mouseLook.x, Vector3.up);
}
}
I found that while the game is running if I uncheck the NavMeshAgent then I will be able to walk around again fine with the keys but when the NavMeshAgent is on and I click on some cube it will keep moving to that cube even if I'm moving with the keys to other places.
I want to be able either using the AgentController and/or the PlayerControllerwhen the game is running.
UPDATE:
I tried to use OnCollisionEnter and OnCollisionExit
But once I set the move to false again on the OnCollisionExit I'm getting the same problem. The reason is that I set the move to true again in the OnCollisionExit is to be able to click the mouse and move again.
So I'm stuck again.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentController : MonoBehaviour
{
public Camera camera;
public NavMeshAgent agent;
public bool move = true;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (Input.GetMouseButtonDown(0) && move == true)
{
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
agent.SetDestination(hit.point);
}
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "HitPoint")
{
move = false;
}
}
private void OnCollisionExit(Collision collision)
{
move = true;
}
}
This probably happened because you're setting the destination to positions inside the cubes, so your agent keeps trying to reach them though it's impossible.
First of all, you may want to tell your agent to stop once one of the movement keys is pressed to avoid conflicts. You can use this to stop:
agent.isStopped = true;
agent.velocity = Vector3.zero;
If this is not enought to solve the problem, one workaround would be to put a Nav Mesh Obstacle Component in your cubes, check Carve and rebake your Nav Mesh. This way the agent would stop in the nearest point from the one you set, outside the cube.
A second option would be to check the collision with the cubes, and tell your agent to stop once it collides with the one you clicked.
Hopefully one of these will work for you!
Hello and thanks for reading this post.
I have 2 objects: A player Object and a JumpButton Object.
The Player object is the player. ofc. The JumpButton Object is an object that I want to make the player jump when you use a touch screen device aka android phone.
This is my Script that is assinged to my JumpButton: using UnityEngine; using System.Collections;
using UnityEngine;
using System.Collections;
public class JumpButtonScript : MonoBehaviour {
public GameObject player;
public GameObject JumpButton;
// Use this for initialization
void Start () {
player = GameObject.Find ("player");
}
// Update is called once per frame
void Update () {
if ((Input.touchCount == 1) && (Input.GetTouch(0).phase == TouchPhase.Began))
{
}
}
}
This is the script that allows me to control the player with arrow keys.
using UnityEngine;
using System.Collections;
public class RobotController : MonoBehaviour {
//This will be our maximum speed as we will always be multiplying by 1
public float maxSpeed = 2f;
public GameObject player;
public GameObject sprite;
//a boolean value to represent whether we are facing left or not
bool facingLeft = true;
//a value to represent our Animator
Animator anim;
//to check ground and to have a jumpforce we can change in the editor
bool grounded = true;
public Transform groundCheck;
public float groundRadius = 1f;
public LayerMask whatIsGround;
public float jumpForce = 300f;
private bool isOnGround = false;
void OnCollisionEnter2D(Collision2D collision) {
isOnGround = true;
}
void OnCollisionExit2D(Collision2D collision) {
anim.SetBool ("Ground", grounded);
anim.SetFloat ("vSpeed", rigidbody2D.velocity.y);
isOnGround = false;
}
// Use this for initialization
void Start () {
player = GameObject.Find("player");
//set anim to our animator
anim = GetComponent <Animator>();
}
void FixedUpdate () {
//set our vSpeed
//set our grounded bool
grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround);
//set ground in our Animator to match grounded
anim.SetBool ("Ground", grounded);
float move = Input.GetAxis ("Horizontal");//Gives us of one if we are moving via the arrow keys
//move our Players rigidbody
rigidbody2D.velocity = new Vector3 (move * maxSpeed, rigidbody2D.velocity.y);
//set our speed
anim.SetFloat ("Speed",Mathf.Abs (move));
//if we are moving left but not facing left flip, and vice versa
if (move > 0 && !facingLeft) {
Flip ();
} else if (move < 0 && facingLeft) {
Flip ();
}
}
void Update(){
if ((isOnGround == true && Input.GetKeyDown (KeyCode.UpArrow)) || (isOnGround == true && Input.GetKeyDown (KeyCode.Space))) {
anim.SetBool("Ground",false);
rigidbody2D.AddForce (new Vector2 (0, jumpForce));
}
if (isOnGround == true && Input.GetKeyDown (KeyCode.DownArrow))
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, 0.2f, 0.2f);
}
if (isOnGround == true && Input.GetKeyUp (KeyCode.DownArrow))
{
gameObject.transform.localScale = new Vector3(transform.localScale.x, 0.3f, 0.3f);
}
}
//flip if needed
void Flip(){
facingLeft = !facingLeft;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
As you can see, then I managed to make the player move on arrow keys, but how can I make it jump when I hit the jump button (touch Screen press), as on an Android Phone?
I really hope you can help me.
If your problem is how to handle input, try this code we will make ray from camera to mousepos in screen and check tag to see what we hitted
public class handleInput: MonoBehaviour {
void Update () {
if (Input.GetMouseButtonDown (0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
if (hit.collider.tag=="jumpbutton"){
jump();
}
}
}
}
}
and for the jumping part you can do this , adding a force to player to send him up and gravity should pull him down
if (grounded == true)
{
rigidbody.AddForce(Vector3.up* jumpSpeed);
grounded = false;
}