Enemies always clumping together in unity 2d - visual-studio

I'm trying to make a top-down shooter game in unity 2d. But the enemies are always clumping together. Does someone knows how to avoid it?
Here's my enemy code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float moveSpeed;
public float stoppingDistance;
public Transform player;
private Rigidbody2D rb;
public GameObject effect;
public int health = 3;
public static int enemyCounter;
public SpriteRenderer enemy;
public Color hurtColor;
void Start() {
rb = GetComponent<Rigidbody2D>();
player = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update() {
enemyCounter = EnemySpawner.enemyCounter;
Vector2 direction = transform.position - player.position;
if (Vector2.Distance(transform.position, player.position) > stoppingDistance) {
transform.position = Vector2.MoveTowards(transform.position, player.position,
moveSpeed * Time.deltaTime);
}
else if (Vector2.Distance(transform.position, player.position) > stoppingDistance) {
transform.position = this.transform.position;
}
}
IEnumerator Flash(){
enemy.color = hurtColor;
yield return new WaitForSeconds(0.01f);
enemy.color = Color.red;
}
void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.tag == "Bullet") {
StartCoroutine(Flash());
health -= 1;
}
if (health <= 0) {
GameObject DestroyEnemy = Instantiate(effect, transform.position, Quaternion.identity);
Destroy(this.gameObject);
Destroy(DestroyEnemy, 2f);
}
}
}
The enemies is moving towards the player, but they clump together when I move the player. I really need help.

If the enemies are moving in straight line to the player they will always clump up. If you want to ensure a minimal distance between enemies you could consider using a second larger collider on another layer but this will might end up looking bad and could make some exploits possible if too big.
The other alternative would be to change the behaviour of the enemies. Making an enemy move depending on the position of the other enemies seems complicated therefore i would simply try to add some randomness to their behaviour. Here are some ideas you could try:
-(would be my first try) switch randomly between two behaviours, 1: move straight to the player (always if very close to player), 2: move in a random direction (sometimes when further away from the player)
-don't give all your enemies the same speed
-make them move towards the player + a small random angle away from the player if far away
-...
To sum it up you will need an improved behaviour and there isn't a single solution.

Related

How to move a cube 3 second in one direction, and next 3 seconds in oppposite direction, alternatively

I am new to unity, and trying something like below, but I can either move only in one direction, or not moving at all.
My cube is a trigger, and is not using gravity. I have checked the Kitematic box. I am trying to make the cube move to and fro, so that player have difficuly collecting it.
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
public class movedanger : MonoBehaviour
{
private int mytime = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
MyMover(mytime);
}
void MyMover(int mytime)
{
if (mytime <= 3)
{
transform.Translate(Vector3.forward * Time.deltaTime);
mytime++;
}
else
{
transform.Translate(-Vector3.forward * Time.deltaTime);
mytime = 1;
}
}
}
What you are looking for is to and fro movement of an object. You can achieve this with Mathf.PingPong() function instead of using translate. I have tested it with a cube, you can set the minimum and maximum distance it should move to and the speed at which it travels. Since you want the cube to move 3 seconds in one direction at a time. You can calculate the speed as distance/time so the max distance it should travel to from the current distance and the time (3 seconds) it takes. Hope this helps.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCube : MonoBehaviour {
public float min = 2f;
public float max = 8f;
public float SpeedOfMovement = 2f;
// Start is called before the first frame update
void Start () {
}
// Update is called once per frame
void Update () {
transform.position = new Vector3 (Mathf.PingPong (Time.time * SpeedOfMovement, max - min) + min, transform.position.y, transform.position.z);
}
}
With InvokeRepeating you will call the same MoveCube method every 3 seconds.
using UnityEngine;
public class MoveDanger: MonoBehaviour
{
public bool isForward = false;
private void Start()
{
InvokeRepeating("MoveCube", 0f, 3f);
}
private void MoveCube()
{
if (isForward)
{
transform.Translate(Vector3.back);
isForward = false;
}
else
{
transform.Translate(Vector3.forward);
isForward = true;
}
}
}
Honestly the best and easiest way to do something like this, once you get used to it, is just to
Use Unity's incredibly simple animator system:
(Essentially just click "new animation" and then drag the object around as you want it animated.)
There are 100s of tutorials online explaining how to use it.
It's one of those things where once you use it and see how easy it is, you will do a "facepalm" and never bother again with other ways.
It's really "the Unity way" to achieve the goal here, dead easy and flexible.

Can I make my enemy run towards the player faster in this code? (Unity)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Enemy : MonoBehaviour
{
public NavMeshAgent Ninja;
public GameObject Player;
public float NinjaDistanceRun = 30.0f;
void Start()
{
Ninja = GetComponent<NavMeshAgent>();
}
void Update()
{
float distance = Vector3.Distance(transform.position, Player.transform.position);
//Run towards player
if(distance < NinjaDistanceRun)
{
Vector3 dirToPlayer = transform.position - Player.transform.position;
Vector3 newPos = transform.position - dirToPlayer;
Ninja.SetDestination(newPos);
}
}
}
The code shown above is what I use to make the enemy follow the player when in range. Can I make my enemy go faster without trashing this whole script and making a new one that has a different line of thought?
Set the maximum speed for your ninja(NavMeshAgent.speed). Here is the documentation for more information:
https://docs.unity3d.com/ScriptReference/AI.NavMeshAgent-speed.html
In the START method you can set a new speed to your Navmesh agent, and this character will walk or run in the player direction.

Unity determining ground via raycasting 2d

Good evening!
I am trying to make a simple 2d platformer in unity without using rigidbodies, so I use raycast to determine where is the ground. I wrote this code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class comtroller : MonoBehaviour
{
private float velocity_y;
private float velocity_x;
private float speed_y = 1;
private float speed_x = 10;
private float localMinimum_y;
//mask of the platform
public LayerMask mask;
void Update()
{
// raycast to sense platforms
RaycastHit2D hit = Physics2D.Raycast((Vector2)transform.position , Vector2.down,Mathf.Infinity, mask);
if (hit)
{
localMinimum_y = hit.point.y;
}
else
{
localMinimum_y = -10;
}
//determine whether the player can fall down
// -0.6 is the distance between the player's transform.position and his legs
if(transform.position.y -0.6f < localMinimum_y)
{
speed_y = 0;
}
else
{
speed_y = 1;
}
//determine velocities
velocity_y = speed_y * Physics2D.gravity.y * Time.deltaTime;
velocity_x = Input.GetAxis("Horizontal") * speed_x * Time.deltaTime;
//movement
transform.Translate(new Vector2(velocity_x, velocity_y));
}
}
However, the player keeps stucking in the platform.
Can anyone help me where is the problem and how can I cure it?
Or if my idea is completely bad can you give any suggestion about how to do it?
Thank you
I recommend following this tutorial if you want to go the non rigidbody way of making a 2d platformer game. It shows you how to manage raycast to make a character move on ground, slope, platforms and many other things. It's really ressourceful and will probably help you a lot.

How to detect a collision between one object and multiple objects in XNA 4.0 C#?

I am new to XNA and CSharp programming so I want to learn to make a treasure hunting game as a beginning so I made a player(as a class) which can walk up, down, left and right. I made a Gem class also which the player can collide with and the gem disappears and a sound is played. But I want to make some walls that the player can collide with and stop so I made a class called Tile.cs (The wall class) and I made a void in it
public void CollideCheck(bool tWalk, bool bottomWalk, bool leftWalk, bool rightWalk, Rectangle topRect, Rectangle bottomRect, Rectangle rightRect, Rectangle leftRect)
{
colRect = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
if (this.colRect.Intersects(topRect))
{
tWalk = false;
}
else
tWalk = true;
if (this.colRect.Intersects(bottomRect))
{
bottomWalk = false;
}
else
bottomWalk = true;
if (this.colRect.Intersects(leftRect))
{
leftWalk = false;
}
else
leftWalk = true;
if (this.colRect.Intersects(rightRect))
{
rightWalk = false;
}
else
rightWalk = true;
}
Then, in the Game1.cs (The main Class) I made an array of "Tiles":
Tile[] tiles = new Tile[5];
And in the update void I made this:
foreach (Tile tile in tiles)
{
tile.CollideCheck(player.topWalk, player.bottomWalk, player.leftWalk, player.rightWalk,
new Rectangle((int)player.Position.X, (int)player.Position.Y - (int)player.Speed.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X, (int)player.Position.Y + (int)player.Speed.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X + (int)player.Speed.X, (int)player.Position.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight),
new Rectangle((int)player.Position.X - (int)player.Speed.X, (int)player.Position.Y, player.currentAnim.FrameWidth, player.currentAnim.FrameHeight));
}
All those rectangles are the borders of the player but when I run the game the player doesn't collide with it so is there any way to fix this?
I can post the project if I am not very clear.
Your parameters are in only, but you set their values inside the call. You have to declare them as out variables so that their value is sent back to the caller. Using out also makes sure you always set a value to them before exiting the function.
So change your function declaration to public void CollideCheck(out bool tWalk, out bool bottomWalk, out bool leftWalk, out bool rightWalk, Rectangle topRect, Rectangle bottomRect, Rectangle rightRect, Rectangle leftRect) and you get the values back.

How to control an animation by touch position along a path in Unity3D?

I have a GameObject that I want to animate along a specific path/curve, but the animation should be controlled by mouse/touch position. So when I touch/click on the GameObject and move the finger/mouse on/near the path (or maybe its easier to just move down) the GameObject should follow its defined path.
I like iTween, but I think it is not possible to find a solution using it here, right?
edit: added image:
It's quite a simpler task than what you might think.
Basically it's a question of remapping a function (that takes the input as parameter) to another function (that express a position along a path).
There are several ways of doing that, depending on the precise effect you want to implement.
The most important choices you have to take are:
How the describe the path/curve
How to handle input
Example
For the path an easy and flexible way is to use some sort of spline curves, such as cubic Bézier curve. It's easy to implement and Unity3D provides built-in functions to draw them. Have a look at Handles.DrawBezier.
Basically a Bézier function takes as input a parameter t in the domain [0,1] and return as a result a point in the space (2D or 3D as you prefer). B(0) gives the point at the begin of the curve, B(1) the end point. (Side note: the function is not linear so in the general case incrementing at a constant rate t doesn't produce a movement at constant speed along the curve. This paper might be useful).
For what concern the input the simpler solution that comes up to my mind is the following:
Accumulate somewhere the vector describing the offset from the position when the touch started to the current touch position. (Here's how to handle touches, have a look at deltaPosition).
Something like:
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
offsetFromStartPos += Input.GetTouch(0).deltaPosition;
}
Let's say you want to swipe up/down your finger for moving forward/back an object along a path.Choose a "travel" distance (the domain of the input function) for your finger in order to complete the movement along the curve and normalize the offset using such distance in order to remap the input into the [0,1] domain.
float t = offsetFromStartPos.y / maxDistanceAlongYAxis;
Vector3 pos = CalculateBezier(t);
transform.position = pos;
It's just an hint to put you in the right direction.
I tried with keyboard and its working fine,
but not with mouse or touch
using System;
using UnityEngine;
public class Collector : MonoBehaviour
{
public Transform startPoint;
public Transform middlePoint;
public Transform endPoint;
public float curveSpeed = 0.5f;
//public float speed = 0f;
private int _direction = 1;
private bool _isObjectSelected;
private Vector3 _mouseLastPosition;
private float _journeyLength;
private Vector3 _offsetPos;
private float _currentTime = 0;
private void Start()
{
_journeyLength = Vector3.Distance(startPoint.position,
endPoint.position);
UpdateJourney(0);
}
private void OnMouseDown()
{
if (_isObjectSelected)
return;
_offsetPos = Vector3.zero;
_mouseLastPosition = Input.mousePosition;
_isObjectSelected = true;
}
private void OnMouseUp()
{
_isObjectSelected = false;
}
private void OnMouseExit()
{
_isObjectSelected = false;
}
private void OnMouseDrag()
{
if (_isObjectSelected)
{
Debug.LogError("Mouse drag");
Vector3 currentPosition = Input.mousePosition;
_offsetPos += currentPosition - _mouseLastPosition;
float distCovered = _offsetPos.y / _journeyLength;
UpdateJourney(distCovered);
_mouseLastPosition = currentPosition;
}
}
private void UpdateJourney(float time)
{
if (time < 0)
time = 0;
else if (time > 1)
time = 1;
_currentTime = time;
transform.position =
QuadraticCurve(startPoint.position,
middlePoint.position,
endPoint.position,
_currentTime);
transform.rotation = Quaternion.Euler(
new Vector3(0, 0,
QuadraticCurve(0, 45, 90, _currentTime)));
}
private void Update()
{
// moving on path using keyboard input
float direction = Input.GetAxisRaw("Horizontal");
if (Math.Abs(direction) > 0.1f)
{
_currentTime += Time.deltaTime * curveSpeed * direction;
UpdateJourney(_currentTime);
}
}
private static Vector3 Lerp(Vector3 start, Vector3 end, float time)
{
return start + (end - start) * time;
}
private static Vector3 QuadraticCurve(Vector3 start, Vector3 middle, Vector3 end, float time)
{
Vector3 point0 = Lerp(start, middle, time);
Vector3 point1 = Lerp(middle, end, time);
return Lerp(point0, point1, time);
}
private static float QuadraticCurve(float start, float middle, float end, float time)
{
float point0 = Mathf.Lerp(start, middle, time);
float point1 = Mathf.Lerp(middle, end, time);
return Mathf.Lerp(point0, point1, time);
}
}

Resources