So I'm using Astar pathfinding project plugin and I have a target Transform, which is a point where the AI builds its path.
Currently I'm trying to implement a roaming state to my enemy. I've decided to create a random point and just shove it to target, but every solution I know or I've found regarding creating a random point is in either Vector2 or Vector3.
How to create a random point and reference it to Transform variable?
I've tried this solution from Astar project's tutorual (Method 1), but that didn't work for me. Here's almost the whole code just in case. The problem is in a state machine in a state Roaming.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class EnemyAI : MonoBehaviour
{
private Transform target; //that's what's all the fuss about
private GameObject player;
public float speed = 200f;
public float nextWaypointDistance = 3f;
public float radius;
private float repeatPath = .5f;
enum State
{
Roaming,
ChaseTarget,
Attacking,
}
private State state;
Path path;
int currentWaypoint;
bool reachedEndOfDestination = false;
private Animator animator;
private float horizontal;
private float vertical;
private Vector2 force;
private GameObject gfx;
private bool setAttackTimer;
private float attackTimer;
Seeker seeker;
Rigidbody2D rb;
private void Awake()
{
//state = State.Roaming;
}
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
gfx = GameObject.Find("CQBgfx");
animator = gfx.GetComponent<Animator>();
player = GameObject.Find("Player");
target = player.transform;
state = State.ChaseTarget;
InvokeRepeating("UpdatePath", 0f, repeatPath);
}
void UpdatePath()
{
if (seeker.IsDone())
seeker.StartPath(rb.position, target.position, OnPathComplete); //Here's the plugin forces me to use Transform
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 1;
reachedEndOfDestination = false;
}
}
void FixedUpdate()
{
Movement();
MovementAnimator();
}
private void Update()
{
switch (state)
{
default:
case State.Roaming:
Vector3 PickRandomPoint () {
var point = Random.insideUnitSphere * radius;
point.y = 0;
point += transform.position;
return point;
}
target = PickRandomPoint(); //Error. Vector3 cannot be converted to Transform.
break;
case State.ChaseTarget:
target = player.transform;
break;
case State.Attacking:
break;
}
}
void Movement()
{
if (path == null)
return;
if (currentWaypoint >= path.vectorPath.Count)
{
reachedEndOfDestination = true;
rb.velocity = Vector2.zero;
force = Vector2.zero;
return;
}
else
{
reachedEndOfDestination = false;
}
if (!reachedEndOfDestination)
{
Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
force = direction * speed * Time.deltaTime;
rb.AddForce(force);
}
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if (distance < nextWaypointDistance)
currentWaypoint++;
}
}
If you absolutely HAVE to use a transform instance, (as I'm guessing you use transform.position to continuously follow a target) creating an empty GameObject and moving that to the target would work.
private GameObject roamTarget; // <-- new roamTarget variable
private Transform target;
private GameObject player;
//...rest of variables
void OnEnable()
{
// create roamTarget instance and reuse it for later
roamTarget = new GameObject(gameObject.name + " Roam Target"); //just gives it a readable name
}
void OnDisable()
{
// destroy our roamTarget on disable so we don't pollute our scene with unused gameobjects
Destroy(roamTarget);
}
and
private void Update()
{
switch (state)
{
default:
case State.Roaming:
roamTarget.transform.position = PickRandomPoint();
target = roamTarget;
break;
//...rest of switch cases
}
//...rest of Update()
}
Just make sure to create one and move it around, instead of constantly instantiating and destroying targets,
Related
I have written a code to get a random transform from my list pathPoints and move my object based on that transform, but what it is doing is getting multiple transforms and trying to move everywhere at once. I want the object to move at one position then get a new position and move there then repeat.
Here is the code:-
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathFinder : MonoBehaviour
[SerializeField] List<Transform> pathPoints;
[SerializeField] Transform pathPrefab;
[SerializeField] float moveSpeed = 10f;
[SerializeField] bool isMoving = false;
[SerializeField] bool isH_Attacking = false;
Animator anim;
Transform defaultWayPoint;
Transform currentTargetPoint;
void Awake()
{
anim = GetComponent<Animator>();
defaultWayPoint = pathPrefab.GetChild(0);
}
void Start()
{
currentTargetPoint = pathPoints[Random.Range(0, pathPoints.Count)];
transform.position = defaultWayPoint.position;
}
void MoveToNextWayPoint()
{
if(transform.position != currentTargetPoint.position)
{
float delta = moveSpeed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, currentTargetPoint.position, delta);
anim.SetBool("isMoving", true);
isMoving = true;
}
GetNextWayPoint();
}
Transform GetNextWayPoint()
{
currentTargetPoint = pathPoints[Random.Range(0, pathPoints.Count)];
Debug.Log(currentTargetPoint.position);
return currentTargetPoint;
}
void Update()
{
MoveToNextWayPoint();
}
*The script is attached to the gameObject which I want to move.
*This is a 2d project.
void MoveToNextWayPoint()
{
if(transform.position != currentTargetPoint.position)
{
float delta = moveSpeed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, currentTargetPoint.position, delta);
anim.SetBool("isMoving", true);
isMoving = true;
}
else
{
GetNextWayPoint();
}
}
I'm using Unity to make my character jump from a moving platform where it goes up & down infinitely. The problem I'm facing is when the moving platform goes up, the jump is working perfectly but when the platform is going down, my character can't jump most often & I can see the platform is "vibrating" a bit which is weird.
Here are my codes:
Moving Platform Script [NB - Rigidbody2D is set to Kinematic]
public class Moveground : MonoBehaviour
{
[SerializeField] private Transform posTop, posBot;
private float maxTop = -0.5f;
private float maxBot = -5.0f;
[SerializeField] private float speed;
[SerializeField] private Transform startPos;
private Vector2 nextPos;
private void Start()
{
nextPos = startPos.position;
}
private void FixedUpdate()
{
if (transform.position == posTop.position)
{
nextPos = posBot.position;
}
if (transform.position == posBot.position)
{
nextPos = posTop.position;
}
transform.position = Vector2.MoveTowards(transform.position, nextPos, speed*Time.deltaTime);
}
}
PlayerController.cs (Only Jump part)
[SerializeField] private LayerMask ground;
private Collider2D coll;
private void Start()
{
coll = GetComponent<Collider2D>();
}
private void Update()
{
InputManager();
}
private void InputManager()
{
if (Input.GetButtonDown("Jump") && coll.IsTouchingLayers(ground)) // Moving Platform's layer is also "ground"
{
Jump();
}
}
private void Jump() {
rb.velocity = new Vector2(rb.velocity.x, jumpforce); // jumpforce is a float number
}
How can I resolve this issue? I'm new to Unity.
Instead of "IsTouchingLayers" try something like this in the PlayerController class:
public bool IsGrounded()
{
return Physics2D.Raycast(transform.position, Vector3.down, 0.1f, ground);
}
and play around with the distance argument, which is the 0.1f one.
If your player transform is not at the bottom of the player, you can also put in something like this instead of 0.1f:
coll.bounds.extents.y + 0.1f
I'm working on a simple 3D game where some balls (fixed Z position) fall along a path (using gravity and physics material) to a small flat platform and "power bounce" off this platform. The player can rotate this platform so I want to recreate a realistic bounce direction according to the platform's angle.
I'm new to coding but so far I've figured the relationship between the vector of the ball as it comes into collision with the platform and the platform's normal, which should be a perpendicular line from the surface and that can be used to reflect the ball's vector to the other direction.
I already used OnCollisionEnter and if statement to detect whether it's the platform you are colliding with, but I don't understand where to indicate the normal of the surface and how to access it. Should it be as a public class in the other object or can it be detected from the ball game object?
I tried some examples from this and other websites and got this far:
public class OnCollision : MonoBehaviour
{
public float speed = 25f;
public Rigidbody rb;
private Rigidbody rigid;
private void Start()
{
rigid = transform.GetComponent<Rigidbody>();
}
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.tag == "BouncePad") {
rb.velocity = transform.up * speed;
}
}
}
Now it bounces off vertically, so I'm guessing I should change the code where the transform.up * speed part is.
Could anyone guide me, please?
Much appreciated.
If you are already using Physics material, look into the Bounciness property. A value of 0 means no bounce, a value of 1 will lead to no loss of energy. The angle of the bounce will be calculated for you. Make sure you drag the physics material onto each object-- both the ball and the wall's material will have an effect.
Finally somebody gave me a hand and came to this solution:
public class Bounce : MonoBehaviour
{
public Rigidbody rb;
public float str = 0.21f;
public float str2 = 0.15f;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody>();
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "BouncePad")
{
rb.AddForce(rb.velocity * str, ForceMode.Impulse);
}
if (col.gameObject.tag == "BouncePad2")
{
rb.AddForce(rb.velocity * str2, ForceMode.Impulse);
}
}
// Update is called once per frame
void Update()
{
}
}
public class BouncTest : MonoBehaviour
{
[SerializeField] private float hight = 3;
[SerializeField] private int times = 5;
[SerializeField] private float speed = 8;
private Vector3 _startPos;
private bool _checkUP;
private int _countTimes;
private float _hightbuf;
[HideInInspector]
public bool _bounceEnd;
private void Awake()
{
_startPos = transform.position;
}
public void TurnOnBounceEffect()
{
_bounceEnd = true;
_checkUP = false;
_hightbuf = hight;
_countTimes = 0;
}
private void FixedUpdate()
{
BounceEffect();
}
private void BounceEffect()
{
if (_bounceEnd)
{
if (!_checkUP)
{
if (transform.position.y <= (_startPos.y + _hightbuf))
transform.position = Vector2.MoveTowards(transform.position, new Vector2(_startPos.x, transform.position.y) + (Vector2.up * _hightbuf), speed * Time.fixedDeltaTime);
else
{
_checkUP = true;
}
}
else if (times != _countTimes)
{
if (transform.position.y > _startPos.y)
transform.position = Vector2.MoveTowards(transform.position, _startPos, speed * Time.fixedDeltaTime);
else
{
_countTimes++;
_checkUP = false;
_hightbuf /= 2;
}
}
else
{
transform.position = Vector2.MoveTowards(transform.position, _startPos, speed * Time.fixedDeltaTime);
if (transform.position.y <= _startPos.y)
{
_bounceEnd = false;
}
}
}
}
}
New to making games in Unity and I've tried to use every way possible to find the answer for this.
How would I add a touch screen function to this C# code in unity to make the player move left and right?
My code
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour
{
private Rigidbody2D rigi;
private Animator anim;
[HideInInspector]
public bool isFacingRight = true;
public float maxSpeed = 7.0f;
public Transform groundCheck;
public LayerMask groundLayers;
private float groundCheckRadius = 0.2f;
private void Awake()
{
rigi = GetComponent<Rigidbody2D>();
anim = this.GetComponent<Animator> ();
}
void Start()
{
}
void Update()
{
}
void FixedUpdate()
{
try
{
float move = Input.GetAxis("Horizontal");
rigi.GetComponent<Rigidbody2D>().velocity = new Vector2
(move * maxSpeed, GetComponent<Rigidbody2D>().velocity.y);
this.anim.SetFloat("Speed",Mathf.Abs(move));
if((move > 0.0f && isFacingRight == false) ||
(move < 0.0f && isFacingRight == true))
{
Flip ();
}
}
catch(UnityException error)
{
Debug.LogError(error.ToString());
}
}
void Flip()
{
isFacingRight = !isFacingRight;
Vector3 playerScale = transform.localScale;
playerScale.x = playerScale.x * -1;
transform.localScale = playerScale;
}
}
You need to use the Input.touches to detect the screen touch.
Maybe in this case you should detect if the player is pressing the right/left side of the screen and moving it accordingly. You can do that by comparing the touch position, something like input.touches[0].position.x < Screen.width/2 to move right for example.
https://docs.unity3d.com/ScriptReference/Input-touches.html
Just a piece of advice, do not use GetComponent inside the update method. In this case you don't actually need it because you already have the references on the awake method.
I created a simple script to move player on Keyboard input, though now I want to move player on touch input, how do I do this ?
Here's my code, so how do I edit this code to make it work ? I have jump working, but dunno how to do it for moving ?
using UnityEngine;
using System.Collections;
public class MoveGround : MonoBehaviour
{
public float y = 0f;
public Rigidbody2D rb;
//public float x = 0f;
//public float z = 0f;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
//move function
if (Input.GetKey(KeyCode.W))
{
rb.velocity = new Vector2(0, y);
}
if (!Input.GetKey(KeyCode.W))
{
rb.velocity = new Vector2(0, 0);
}
if (Input.GetKey(KeyCode.S))
{
rb.velocity = new Vector2(0, -y);
}
//move function end
}
public void Move()
{
}
}
How do you want to move your character using touch input instead of keyboard input? Your current code moves the character depending on whether the "W" or "S" keys are being pushed.
You could make two UI buttons that correspond to "forward" and "backward" keys.
Add an Event Trigger component to those buttons. Add two event types to the buttons: "Pointer Down" and "Pointer Up."
Add a function to the list of each of those events.
Put this code onto an object in your scene.
bool movingForward;
bool movingBackward;
public float speed = 0f;// set this either here in code or in the editor
public Rigidbody2D rb;
void Start (){
rb = GetComponent<Rigidbody2D>();
movingForward = false;
movingBackward = false;
}
// Your forward button will call this function
public void moveForward (){
movingForward = true;
movingBackward = false;
}
// Your backwardbutton will call this function
public void moveBackward (){
movingForward = false;
movingBackward = true;
}
//
public void stopMoving(){
movingForward = false;
movingBackward = false;
}
void Update () {
if(movingForward || movingBackward ){// we're moving
if(movingForward ){// forward
rb.velocity = new Vector2(0, speed);
}else if(movingBackward ){// backward
rb.velocity = new Vector2(0, -speed);
}
}else{// we're moving neither forward nor backward
rb.velocity = new Vector2(0, 0);// so stand still
}
}
Point each function to that object. In the Pointer Down event, choose the "moveForward" function for the button you want to move your character forward, and the "moveBackward" function for the other button.
In the Pointer Up event for both, just choose the "stopMoving" function.
It's really simplistic, but it will work.
I agree with #PolakięGames. The best way to do it is creating two UI buttons with EventTriggers attached and assigning the methods MoveForward and MoveBackwards to PointerDown and StopMoving to PointerDown. Although I would implement it differently:
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class MoveExample : MonoBehaviour {
public float Acceleration = 4f;
public float Speed = 4f;
private Vector2 _velocity = Vector2.zero;
private Rigidbody _rigidbody;
private void Start() {
_rigidbody = GetComponent<Rigidbody>();
}
// movement methods
public void Move(float velocity) { _velocity.x = velocity * Speed; }
public void MoveForward() { Move(1f); }
public void MoveBackwards() { Move(-1f); }
public void StopMoving() { Move(0f); }
private void Update() {
_rigidbody.velocity = Vector2.Lerp(_rigidbody.velocity, _velocity, Time.deltaTime * Acceleration);
}
}
That way your object will move smootly and you can control the speed and acceleration. You have to change Rigidbody to Rigidbody2D if you intend to use it with 2D Physics.