how to change turn speed AI enemy - c#

I have enemy AI and patrol points to which he moves, when he reaches the point, he starts turning to the next point and moves towards it. So it turns too slowly, how to speed up this turn?
I used transform.LookAt() but it rotates instantly.
[SerializeField] private NavMeshAgent _agent;
[SerializeField] private List<Transform> _targets;
private float _patrolDistance;
private int index;
private void FixedUpdate()
{
_patrolDistance = Vector3.Distance(_agent.transform.position, _agent.pathEndPosition);
if (_patrolDistance < 0.1f)
{
RandomTarget();
transform.LookAt(_targets[index]);
}
MovePatrolPoint();
}
private void RandomTarget()
{
index = Random.Range(0, _targets.Count);
}
private void MovePatrolPoint()
{
_agent.SetDestination(_targets[index].position);
}
}

You can use alternatives like Quaternion.Slerp and Quaternion.LookRotation for smoother rotations

I would do something like this with a slerp and controlling the speed with a variable multiplied times the delta time.
[SerializeField] private NavMeshAgent _agent;
[SerializeField] private List<Transform> _targets;
[SerializeField] private float Speed = 1;
private float _patrolDistance;
private Transform TargetTransform;
private Vector3 Target => TargetTransform.position;
private void Update()
{
CheckTargetDistance();
}
private void CheckTargetDistance()
{
_patrolDistance = Vector3.Distance(_agent.transform.position, _agent.pathEndPosition);
if (_patrolDistance < 0.1f)
{
RandomTarget();
}
}
private void FixedUpdate()
{
LookAtTarget();
MovePatrolPoint();
}
private void RandomTarget()
{
int index = Random.Range(0, _targets.Count);
TargetTransform = _targets[index];
}
private void MovePatrolPoint()
{
_agent.SetDestination(Target);
}
private void LookAtTarget()
{
Quaternion lookRotation = Quaternion.LookRotation(Target - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Speed * Time.deltaTime);
}

Related

Formula to deal damage - armor

I'm just starting to learn c# and got stuck in this exercise.
I need to create a formula to deal damage - armor. The armor is reduced with each hit. If I write
float damage = hp+armor-(physicalDamage-damageStrength)it doesn't damage the enemy. The task says I should change only `float damage = ...' for this code to work. So, the mistake can be only in this line float damage =
[RequireComponent(typeof(Animator))]
public class Goblin : MonoBehaviour, IDamagable {
static private string hitTriggerName = "Hit";
[SerializeField] private float hp = 800; // health
[SerializeField] private float armor = 100; // armor
[SerializeField] private float armorStrength = 5; // armor decrease each time
[SerializeField] private int n = 0; // number of hits
private Animator selfAnimator;
private void Awake() {
selfAnimator = GetComponent<Animator>();
}
public void ApplyDamage(float physicalDamage, float damageStrength) {
float damage = hp+armor-physicalDamage-damageStrength;
if (damage < 0) {
hp += damage;
}
n += 1;
armor -= armorStrength;
selfAnimator.SetTrigger(hitTriggerName);
}
public void onHitAnimationEnd() {
if (hp <= 0) {
Kill();
}
}
private void Kill() {
Destroy(gameObject);
}
}```
***Corersponding part of a "player" object***
```using System;
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class Player : MonoBehaviour {
static private string attackTriggerName = "Attack";
[SerializeField] private float attackCooldown = 0.1f;
[SerializeField] private Goblin aim;
private float timeToNextAttack = 0f;
private Animator selfAnimator;
private void Awake() {
selfAnimator = GetComponent<Animator>();
}
[Serializable] private class Staff {
[SerializeField] private float physicalDamage = 100; // staff damage
[SerializeField] private float damageStrength = 5; // damage reduction each time
public void Use(IDamagable aim) {
if (aim != null) {
aim.ApplyDamage(physicalDamage, damageStrength);
}
}
}
[SerializeField] private Staff weapon;
private void Attack() {
selfAnimator.SetTrigger(attackTriggerName);
weapon.Use(aim);
timeToNextAttack = attackCooldown;
}
private void Update() {
if (Input.GetKeyDown(KeyCode.Space) && timeToNextAttack <= 0) {
Attack();
}
timeToNextAttack -= Time.deltaTime;
}
}```
I may be wrong or didn't understand, but by quickly looking at the code, I'd suggest you declare the damage variable above the Awake function. And give it a 0.0f value by default.
I think you made two unrelated mistakes:
damage sould be only: float damage = armor-physicalDamage
damageStrength should not be a parameter of the method because it should be applied to the staff physicalDamage after the hit, like you did to the armor.

How to make a random point and reference it to Transform?

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,

Object not touching ground when moving platform is falling in Unity

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

Unity how to "power bounce" one object from another relative to it's angle?

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;
}
}
}
}
}

When i destroy my object create new one in diffrent position

When i click on my mouse button and destroy GameObject i want to create new one on random position, i try instatiate and other methods but it didn't work can someone help me whit this?
public GameObject tapObject;
private float respawnTime = 1f;
public float xMin;
public float xMax;
public float yMin;
public float yMax;
void Start()
{
StartCoroutine(spawnEnemyTime());
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Destroy(tapObject);
}
}
private void RandomSpawnObject()
{
tapObject.transform.position = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
}
IEnumerator spawnEnemyTime()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
RandomSpawnObject();
}
}
If you want to keep the same GameObject you can avoid destroying it, instead you can control if it's active or not. It should look like this:
Edit:
Using GameObject.SetActive()
public GameObject tapObject;
private float respawnTime = 1f;
public float xMin;
public float xMax;
public float yMin;
public float yMax;
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
tapObject.SetActive(false);
StartCoroutine(spawnEnemyTime());
}
}
private void RandomSpawnObject()
{
tapObject.SetActive(true);
tapObject.transform.position = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
}
IEnumerator spawnEnemyTime()
{
yield return new WaitForSeconds(respawnTime);
RandomSpawnObject();
}
Using GameObject.Instantiate()
public GameObject prefab;
public GameObject tapObject;
private float respawnTime = 1f;
public float xMin;
public float xMax;
public float yMin;
public float yMax;
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Destroy(tapObject);
StartCoroutine(spawnEnemyTime());
}
}
private void RandomSpawnObject()
{
tapObject = GameObject.Instantiate(prefab, new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax)), Quaternion.identity);
}
IEnumerator spawnEnemyTime()
{
yield return new WaitForSeconds(respawnTime);
RandomSpawnObject();
}
Note that when using GameObject.Instantiate(), you need to have a prefab attached.
The simple way to resolve your problem is to create a method and call it with a timer and in that method just use following code
Code
Vector3 position = new Vector3(Random.Range(-10.0f, 10.0f), 0, Random.Range(-10.0f, 10.0f));
Instantiate(prefab, position, Quaternion.identity);
Note
Instead of using the prefab use you can use the gameobject you are using in the application

Categories