Я слежу за созданием игры с препятствиями из Game Programming with Unity и C# Кейси Хардмана. На первых нескольких страницах главы 16 я дошел до того, что вы создаете Опасность, чтобы убить игрока. На начальных этапах вы пишете код, который убивает объект игрока, если объект игрока сталкивается с опасностью. Я следовал инструкциям (насколько я могу судить) до буквы, и все же, когда я создал сферу в качестве тестовой опасности, назначил ей сценарий и запустил в нее объект игрока, ничего не произошло, когда они столкнулись. Я подумал, что, возможно, была ошибка с кодом Hazard, поэтому я закомментировал код «при столкновении с объектом на уровне игрока, убить игрока», написал код, чтобы он просто писал в консоль, когда они сталкиваются, и протестировал его. К сожалению, когда эти два объекта соприкасаются друг с другом, похоже, не происходит обнаружения столкновений. Я погуглил «объекты, не сталкивающиеся с единством», и все комбинации «столкновение, не обнаруженное в единстве», которые я могу придумать, и ни один из ответов не помог, поэтому я публикую здесь в надежде получить ответ. Я включаю скриншоты двух объектов и их настроек, мои настройки физики в Unity и код, который я написал для обоих объектов, в надежде, что кто-нибудь поймет, что я делаю неправильно.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
//References
[Header("References")]
public Transform trans;
public Transform modelTrans;
public CharacterController characterController;
//Movement
[Header("Movement")]
[Tooltip("Units moved per second at maximum speed.")]
public float movespeed = 24;
[Tooltip("Time, in seconds, to reach maximum speed.")]
public float timeToMaxSpeed = .26f;
private float VelocityGainPerSecond{ get { return movespeed / timeToMaxSpeed; }}
[Tooltip("Time, in seconds, to go from maximum speed to stationary.")]
public float timeToLoseMaxSpeed = .2f;
private float VelocityLossPerSecond { get { return movespeed / timeToLoseMaxSpeed; }}
[Tooltip("Multiplier for momentum when attempting to move in a direction opposite the current traveling direction (e.g. trying to move right when already moving left.")]
public float reverseMomentumMultiplier = 2.2f;
private Vector3 movementVelocity = Vector3.zero;
private void Movement()
{
// IF W or the up arrow key is held:
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
if (movementVelocity.z >= 0) // If we're already moving forward
//Increase Z velocity by VelocityGainPerSecond, but don't go higher than 'moveSpeed':
movementVelocity.z = Mathf.Min(movespeed,movementVelocity.z + VelocityGainPerSecond * Time.deltaTime);
else // Else if we're moving back
//Increase Z velocity by VelocityGainPerSecond, using the reverseMomentumMultiplier, but don't raise higher than 0:
movementVelocity.z = Mathf.Min(0,movementVelocity.z + reverseMomentumMultiplier * Time.deltaTime);
}
//If S or the down arrow key is held:
else if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
if (movementVelocity.z > 0) // If we're already moving forward
movementVelocity.z = Mathf.Max(0,movementVelocity.z - VelocityGainPerSecond * reverseMomentumMultiplier * Time.deltaTime);
else // If we're moving back or not moving at all
movementVelocity.z = Mathf.Max(-movespeed,movementVelocity.z - VelocityGainPerSecond * Time.deltaTime);
}
else //If neither forward nor back are being held
{
//We must bring the Z velocity back to 0 over time.
if (movementVelocity.z > 0) // If we're moving up,
//Decrease Z velocity by VelocityLossPerSecond, but don't go any lower than 0:
movementVelocity.z = Mathf.Max(0,movementVelocity.z - VelocityLossPerSecond * Time.deltaTime);
else //If we're moving down,
//Increase Z velocity (back towards 0) by VelocityLossPerSecond, but don't go any higher than 0:
movementVelocity.z = Mathf.Min(0,movementVelocity.z + VelocityLossPerSecond * Time.deltaTime);
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
if (movementVelocity.x >= 0) //If we're already moving right
//Increase X velocty by VelocityGainPerSecond, but don't go higher than 'movespeed':
movementVelocity.x = Mathf.Min(movespeed,movementVelocity.x + VelocityGainPerSecond * Time.deltaTime);
else //If we're moving left
//Increase X velocity by VelocityGainPerSecond, using the reverseMomentumMultiplier, but don't raise higher than 0:
movementVelocity.x = Mathf.Min(0,movementVelocity.x + VelocityGainPerSecond * reverseMomentumMultiplier * Time.deltaTime);
}
else if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
if (movementVelocity.x > 0) //If we're already moving right
movementVelocity.x = Mathf.Max(0,movementVelocity.x - VelocityGainPerSecond * reverseMomentumMultiplier * Time.deltaTime);
else // If we're moving left or not at all
movementVelocity.x = Mathf.Max(-movespeed,movementVelocity.x - VelocityGainPerSecond * Time.deltaTime);
}
else //If neither left nor right are being held
{
//We must bring the X Velocity back to 0 over time.
if (movementVelocity.x > 0) //If we're moving right,
//Decrease X velocity by VelocityLossPerSecond, but don't go any lower than 0:
movementVelocity.x = Mathf.Max(0,movementVelocity.x - VelocityLossPerSecond * Time.deltaTime);
else //If we're moving left
//Increase X velocity (back towards 0) by VelocityLossPerSecond, but don't go any higher than 0:
movementVelocity.x = Mathf.Min(0,movementVelocity.x + VelocityLossPerSecond * Time.deltaTime);
}
//If the player is moving in either direction (left/right or up/down):
if (movementVelocity.x != 0 || movementVelocity.z != 0)
{
//Applying the movement velocity:
characterController.Move(movementVelocity * Time.deltaTime);
//Keeping the model holder rotated towards the last movement direction:
modelTrans.rotation = Quaternion.Slerp(modelTrans.rotation, Quaternion.LookRotation(movementVelocity), .18F);
}
}
//Death and Respawning
[Header("Death and Respawning")]
[Tooltip("How long after the player's death, in seconds, before they are respawned?")]
public float respawnWaitTime = 2f;
private bool dead = false;
private Vector3 spawnPoint;
private Quaternion spawnRotation;
private void Update()
{
Movement();
}
void Start()
{
spawnPoint = trans.position;
spawnRotation = modelTrans.rotation;
}
public void Die()
{
if (!dead)
{
dead = true;
Invoke("Respawn", respawnWaitTime);
movementVelocity = Vector3.zero;
enabled = false;
characterController.enabled = false;
modelTrans.gameObject.SetActive(false);
}
}
public void Respawn()
{
modelTrans.rotation = spawnRotation;
dead = false;
trans.position = spawnPoint;
enabled = true;
characterController.enabled = true;
modelTrans.gameObject.SetActive(true);
}
}
`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hazard : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.layer == 8)
{
//Player player = other.GetComponent<Player>();
//if (player != null)
//player.Die();
Debug.Log("Yay!");
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}`
Я пытался:
Добавление отдельного коллайдера коробки к объекту игрока
Добавление Rigidbody к одному, обоим и другим объектам
Выдергивая мои волосы
Перезапуск единства
Перезагружаю компьютер
Пометка объекта Player как Player (я увидел вопрос, похожий на мой, поэтому подумал, что, может быть, это поможет; я еще не очень хорошо знаком с Unity или C#, поэтому я не уверен, что и почему что-то может помочь.)
Ничего из вышеперечисленного не создавало впечатления, что Unity обнаруживает столкновение между этими двумя объектами.





Для OnTriggerEnter() нужны коллайдеры с галочкой Is Trigger и твердое тело. Но этот чек
if (other.gameObject.layer == 8)
не то, на что это похоже. Значения слоя используются как битовые маски, подробнее об этом здесь. Чтобы проверить правильный слой, вам нужны побитовые операции:
int playerMask = LayerMask.GetMask("Player"); // something like 00110101001
int otherLayer = 1 << other.gameObject.layer; // something like 00010000000
if ((playerMask | otherLayer ) != 0) { // we hit the mask ...1.......
Debug.Log("yay");
}
Будьте осторожны при использовании побитовых операторов, чтобы не быть обманутыми приоритетами операторов. Короткая версия сверху будет
if ((playerMask | (1 << other.gameObject.layer)) != 0) { ... // extra parentheses!
Это сработает для всех объектов в слое игрока. Альтернативой может быть установка тега объекта игрока и сравнение с этим тегом:
if (other.gameObject.CompareTag("Player")) { ... // "Player" tag is not the same as "Player" layer
Вот строка из документации для OnTriggerEnter:
"Both GameObjects must contain a Collider component. One must have Collider.isTrigger enabled, and contain a Rigidbody."
Пробовали ли вы добавить Collider к своему игроку одновременно с компонентом Rigidbody к вашей опасности?
Это сделало это! Большое спасибо! Клянусь, в книге ничего не сказано о наличии коллайдера на объекте Player или Rigidbody на Hazard, не говоря уже об обоих, но, похоже, это помогло. Я не могу передать вам, насколько я ценю это предложение!
Круто, я рад, что это сработало! Пожалуйста, пометьте ответ как принятый, и мы можем закрыть вопрос
Пока ничего не меняя в коде, я добавил Box Collider к объекту Player И Rigid Body к объекту Hazard, и это, похоже, помогло. Возможно, я где-то пропустил шаг, который заставил меня добавить Box Collider к объекту Player, потому что я вообще не помню, чтобы этот шаг был указан в списке (на самом деле, автор говорит, что они нам не нужны в дочерних объектах Base и Top объекта). Player, потому что, имея CharacterController в объекте Player, нам также не нужен коллайдер, но что угодно). Тем не менее, спасибо за это понимание!