I have some problems with a script for a "Field Of View" of the enemy. After watching a tutorial to create a field of view for the player I thought I can switch it to the enemy's so that they detect the player and do some other stuff. I created a boolean variable playerInRange to detect if the enemies can detect the player and set this variable to true or false.
It works fine with just one enemy. When I add another one, the new enemy will not detect the player. So maybe it is related to the coroutine, but I am not sure.
Here is a little bit of my code:
void Start() {
StartCoroutine("FindTargetsWithDelay", .2 f);
}
IEnumerator FindTargetsWithDelay(float delay) {
while (true) {
yield
return new WaitForSeconds(delay);
FindVisibleTargets();
}
}
public void FindVisibleTargets() {
visibleTargets.Clear();
Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
for (int i = 0; i < targetsInViewRadius.Length; i++) {
Transform target = targetsInViewRadius[i].transform;
Vector3 dirToTarget = (target.position - transform.position).normalized;
if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2) {
float dstToTarget = Vector3.Distance(transform.position, target.position);
if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask)) {
// Not so nice solution!
// The movement should be in a separate script!
visibleTargets.Add(target);
nav.SetDestination(player.position);
anim.SetBool("IsRunning", true);
if (dstToTarget < attackRange) {
playerInRange = true;
Debug.Log(playerInRange);
}
}
} else {
anim.SetBool("IsRunning", false);
playerInRange = false;
Debug.Log(playerInRange);
}
}
}
Thank you guys for your little hint. It was really a little hierarchy problem :( Sorry for that newbie/DAU question.
Cheers
Nico
Related
I am working on a small game for a school project, in which my player needs to attack enemies in a level. My plan is to have a collider that is enabled in an attached script, and then disabled when the attack is done. My current problem is that that the collider does not flip the way it is supposed to, it seems to flip directly on the overall x axis instead of flipping in the x axis related to the player. It is a child of the player so I am clueless as to why it is doing this. Any solutions or other approaches would be greatly appreciated. I will attach the current script that controls the collider below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VerticalSword : MonoBehaviour
{
//Variables use to active the colliders
Collider2D AttackColliderVertical;
//Variables for the location of the collider
Vector2 attackOffset;
private void Start()
{
AttackColliderVertical = GetComponent<Collider2D>();
attackOffset = transform.position;
AttackColliderVertical.enabled = false;
}
public void FixedUpdate()
{
attackOffset = transform.position;
}
public void AttackUp()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = attackOffset;
}
else if (attackOffset.y < 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1)); //I think the problem is somewhere in this if and else if statement
}
print("Attack up successful"); //Error checking (This works when run)
}
public void AttackDown()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1));
}
else if (attackOffset.y < 0)
{
transform.position = attackOffset; //I think the problem is somewhere in this if and else if statement
}
print("Attack down successful"); //Error checking (This works when run)
}
public void StopAttack()
{
AttackColliderVertical.enabled = false;
}
}
Use transform.localPosition, not transform.position (that's its world space position). You need to change it everywhere in this script; the Start() function and the two attack functions
So I'm trying to implement shooting in a multiplayer game. I want to spawn a bullet out of a weapon. On the server side it works, but as I shoot as a client it says [Netcode] Behaviour index was out of bounds. Did you mess up the order of your NetworkBehaviours?.
The bullet is a prefab which I included in NetworPrefabs. The prefab also has a Network Object component.
This is where I call shoot()
if(shot == false && transform.GetChild(0).GetComponent<Weapon>().bulletInMag > 0 && reloading == false)
{
shot = true;
StartCoroutine(shoot());
}
This is what shoot() does
IEnumerator shoot()
{
firePoint = transform.GetChild(0).GetChild(0).transform;
weapon = transform.GetChild(0).gameObject.GetComponent<Weapon>();
if (weapon.threeShot)
{
for(int i=0; i<3; i++)
{
if(NetworkManager.Singleton.IsServer)
{
spawnBullet();
} else
{
spawnBulletServerRpc();
}
weapon.bulletInMag -= 1;
SharedFunctions.Instance.updateAmmoCount();
yield return new WaitForSeconds(0.2f);
}
yield return new WaitForSeconds(weapon.shootingIntervall);
shot = false;
}
else
{
if (NetworkManager.Singleton.IsServer)
{
spawnBullet();
}
else
{
spawnBulletServerRpc();
}
weapon.bulletInMag -= 1;
SharedFunctions.Instance.updateAmmoCount();
yield return new WaitForSeconds(weapon.shootingIntervall);
shot = false;
}
}
void spawnBullet()
{
GameObject bullet = Instantiate(prefabBullet, firePoint.position, transform.rotation);
bullet.GetComponent<Rigidbody2D>().AddForce(firePoint.up * weapon.bulletSpeed, ForceMode2D.Impulse);
bullet.GetComponent<NetworkObject>().Spawn();
}
[ServerRpc]
void spawnBulletServerRpc()
{
spawnBullet();
}
I'm thankful for any help.
I finally fixed the issue. The error [Netcode] Behaviour index was out of bounds. Did you mess up the order of your NetworkBehaviours? basically means that there is a mismatch between server and client representation of a network object.
At the beginning of my Player script I wrote:
public override void OnNetworkSpawn()
{
if (!IsOwner) Destroy(this);
}
which deletes the component if your not the owner.
So when I joined I had the script on client side but it wasn't there on server side. So when I tried to make an ServerRpc it didn't know where to send it. The same goes for ClientRpc.
A very helpful source was the Unity Mulitplayer Networking Discord: https://discord.gg/buMxnnPvTb
I'm working on a sports game where if a player skates into the goalie crease, I want all the player positions to reset to the center of the ice and do a three second countdown before play resumes.
I have tried to hardcode the starting position for the main player in a variable called PlayerStart and I call Player.transform.position = PlayerStart. When I did this, the player didn't move so I tried to switch the object I was setting as the player. This did what I wanted, but the mouse functionality changes for some reason and when the countdown ends, the player just goes right back to the position they were in before the crease violation was called.
Other things I've tried:
transform.SetPositionAndRotation
PlayerStart = Player.transform.position (instead of hard coding the numbers in)
Here is my code:
public class Crease : MonoBehaviour
{
private float Delay = 0;
private bool CreaseViolated = false;
private GameObject Player;
public Vector3 PlayerStart;
// Start is called before the first frame update
void Start()
{
Ring = GameObject.Find("Ring");
Player = GameObject.Find("FPSController");
PlayerStart = new Vector3(29.75f, 6.03999996f, 4.42000008f);
}
// Update is called once per frame
void Update()
{
Delay -= Time.deltaTime;
if (CreaseViolated)
{
CreaseViolation();
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name != "Ring"
&& other.gameObject.name != "TeamGoalie"
&& other.gameObject.name != "OpponentGoalie")
{
CreaseViolated = true;
Delay = 3;
}
}
void CreaseViolation()
{
if (Delay > 0)
{
PlayerTip.GetComponent<PickupRing>().HasRing = false;
Opponent.GetComponent<AI>().HasRing = false;
Ring.transform.parent = null;
}
else
{
text.text = " ";
if (CreaseViolated)
{
Debug.Log("Player position before: " + Player.transform.position);
Player.transform.position = PlayerStart;
Debug.Log("Player position after: " + Player.transform.position);
//Player.transform.SetPositionAndRotation(PlayerStart + new Vector3(0f, 0.800000012f, 0f), new Quaternion(0f, -0.707106829f, 0f, 0.707106829f) + new Quaternion(0f, 0f, 0f, 1f));
GameObject.Find("Countdown").GetComponent<CountdownText>().timeRemaining = 4;
CreaseViolated = false;
}
}
}
}
Here is a short YouTube video showing my code and the demo: https://www.youtube.com/watch?v=mZt_4AppBh8
this problem is all solved now thanks to an awesome person at my university helping me out! The solution was disabling the CharacterController before repositioning the player and then enabling it again after.
So this:
Player.transform.position = PlayerStart;
in the CreaseViolation function becomes
cc.enabled = false;
PlayerController.transform.position = PlayerControllerStart;
cc.enabled = true;
with cc being declared earlier as
private CharacterController cc;
and in the start function I assigned it with the value
cc = Player.GetComponent<CharacterController>();
with PlayerController being set to the FPSController.
I renamed Player to PlayerController for more clarity.
Hopefully this helps anyone having the same problem I was having!
The Above answer works perfectly but might bring some issues when doing networked changes in player transform positions.
The reason the transform change doesn't work without disabling the Character Controller is because the character controller overrides the transforms.
To Disable this and enable an Auto Synchronization between transforms GoTo:
Edit > Project Settings > Physics > (Enable Auto Sync Transforms)
I have looked all over internet, but I cannot find a solution to my problem. I am trying to create a game and it has 3 scenes: the Game Start, Main and Game over.
The problem is that when trying to load the main level from other scenes it does not do what it has to do (e.g. jump) and the scene itself is lagging. When I try to load only the Main Scene it works ok, but after the character dies and the scene is reloaded, it starts to lag again and I cannot jump or do anything it is supposed to do.
Any ideas on what the problem might be?
using UnityEngine;
using System;
public class Player : MonoBehaviour
{
// The force which is added when the player jumps
// This can be changed in the Inspector window
public Vector2 jumpForce = new Vector2(0, 300);
private bool shouldJump = true;
// Update is called once per frame
private float jumpThreshold = 0.5f;
private float previousJumpTime;
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
// Die by collision
void OnCollisionEnter2D(Collision2D other)
{
Die();
}
void Die()
{
Application.LoadLevel ("main");
}
}
You said your level is called Main, but in the code you are loading "main", i'm not sure if that's the problem, but you seem to be loading the level correctly, so check if is main or Main the exact name of the level.
Also, when compiling, make sure you have all levels checked
With provided data its impossible to say what causes low performance, but I recommend you to use Profiler tool (can be found in personal version of Unity 5) and figure out what scripts and functions are problematic.
Also, try to avoid calling GetComponent<Rigidbody2D>() in Update/FixedUpdate/LateUpdate and cache this component instead.
I second what Utamaru said.
Rigidbody2D myRigidbody2d;
void Start ()
{
myRigidbody2d = GetComponent<Rigidbody2D>(); //Do this once
}
Inside your fixed update, you can do this:
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
myRigidbody2d.velocity = Vector2.zero;
myRigidbody2d.AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
I can't see the rest of your code so I am not really sure that is the problem but give this a try.
I have created a random generator for a prefab called "Tunnel". Whenever i collide with a tunnel prefab, i wish to keep the game running. If i am no longer colliding with a tunnel i wish the game to stop.
The game works fine for the first tunnel, but when i reach the end of the first tunnel (start of the second - they overlap), my "OnTriggerExit2D" function stops the game.
Is there a way to tell my "OnTriggerExit2D" to check if i'm colliding with a different tunnel?
Here's my code:
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log (other.gameObject.tag);
if (other.gameObject.CompareTag ("LeftTunnel")) {
touchRef.onTunnelL = true;
}
if (other.gameObject.CompareTag ("RightTunnel")) {
touchRef.onTunnelR = true;
}
}
void OnTriggerExit2D(Collider2D other)
{
if(other.gameObject.CompareTag("LeftTunnel"))
{
touchRef.OnTriggerExit2Dchild();
touchRef.onTunnelL = false;
}
if(other.gameObject.CompareTag("RightTunnel"))
{
touchRef.OnTriggerExit2Dchild();
touchRef.onTunnelR = false;
}
}
thanks!
So, after consulting with some people I decided to change the whole mechanism to work with ray casting and it works great now.
Here's the code for anyone in the future (I call cast function on Update):
void Cast()
{
for (int i = 0; i < Input.touchCount; ++i)
{
Vector2 test = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position);
RaycastHit2D hit = Physics2D.Raycast(test, (Input.GetTouch(i).position));
Vector2 touchPos = new Vector2(test.x, test.y);
onLeft(touchPos);
onRight(touchPos);
}
}
void onLeft(Vector2 touchPos){
Collider2D tempL;
if (tempL = Physics2D.OverlapPoint (touchPos)) {
if(tempL.CompareTag("LeftTunnel")){
Debug.Log ("Hit Left tunnel");
onTunnelL = true;
}
}
else{
onTunnelL = false;
}
}
void onRight(Vector2 touchPos){
Collider2D tempR;
if (tempR = Physics2D.OverlapPoint (touchPos)) {
if(tempR.CompareTag("RightTunnel")){
Debug.Log ("Hit Right tunnel");
onTunnelR = true;
}
}
else{
onTunnelR = false;
}
}