Opposer Vr Script -
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
using Unity.XR.CoreUtils;
public class OpposerVR : MonoBehaviour
[Header("Movement Settings")]
[SerializeField] private float chaseSpeed = 3.5f;
[SerializeField] private float patrolSpeed = 1.5f;
[SerializeField] private float stoppingDistance = 2.0f;
[SerializeField] private float chaseRange = 10.0f;
[SerializeField] private float attackRange = 2.0f;
[SerializeField] private float patrolRadius = 15.0f;
[SerializeField] private float waitTimeAtPatrolPoint = 2.0f;
[Header("Combat Settings")]
[SerializeField] private int maxHealth = 100;
[SerializeField] private int attackDamage = 15;
[SerializeField] private float attackCooldown = 1.5f;
[SerializeField] private float attackAnimDuration = 0.5f;
[SerializeField] private GameObject projectilePrefab;
[SerializeField] private Transform projectileSpawnPoint;
[SerializeField] private bool isRangedAttacker = false;
[Header("Senses")]
[SerializeField] private float fieldOfView = 110f;
[SerializeField] private float hearingRadius = 12f;
[SerializeField] private LayerMask obstructionMask;
[SerializeField] private LayerMask playerLayer;
[Header("Behavior")]
[SerializeField] private bool useCover = true;
[SerializeField] private float coverCheckInterval = 1.5f;
[SerializeField] private float fleeHealthThreshold = 25f;
[Header("Audio")]
[SerializeField] private AudioClip[] attackSounds;
[SerializeField] private AudioClip[] hurtSounds;
[SerializeField] private AudioClip[] deathSounds;
[SerializeField] private AudioClip[] spottedSounds;
[SerializeField] private float audioVolume = 0.7f;
// Components
private NavMeshAgent agent;
private Animator animator;
private AudioSource audioSource;
private Transform player;
private XROrigin xrOrigin;
private int currentHealth;
private bool isDead = false;
private bool isAttacking = false;
private float nextAttackTime = 0f;
private float nextCoverCheck = 0f;
private Vector3 lastKnownPlayerPosition;
private bool playerSpotted = false;
private bool isInvestigating = false;
private Vector3 investigationPoint;
private Vector3 currentCoverPoint;
// Patrol variables
private Vector3 patrolTarget;
private bool isWaiting = false;
// States
private enum AIState Patrol, Chase, Attack, Investigate, Flee, TakeCover
private AIState currentState = AIState.Patrol;
void Start()
// Get components
agent = GetComponent<NavMeshAgent>();
animator = GetComponent<Animator>();
audioSource = GetComponent<AudioSource>();
if (audioSource == null)
audioSource = gameObject.AddComponent<AudioSource>();
// Find player
xrOrigin = FindObjectOfType<XROrigin>();
if (xrOrigin != null)
player = xrOrigin.transform;
currentHealth = maxHealth;
agent.speed = patrolSpeed;
// Set initial patrol point
SetNewPatrolPoint();
// Start coroutines
StartCoroutine(SensePlayerRoutine());
if (useCover)
StartCoroutine(CoverCheckRoutine());
void Update()
if (isDead) return;
// State machine
switch (currentState)
case AIState.Patrol:
PatrolBehavior();
break;
case AIState.Chase:
ChaseBehavior();
break;
case AIState.Attack:
AttackBehavior();
break;
case AIState.Investigate:
InvestigateBehavior();
break;
case AIState.Flee:
FleeBehavior();
break;
case AIState.TakeCover:
TakeCoverBehavior();
break;
// Update animator
UpdateAnimations();
#region Behavior Methods
private void PatrolBehavior()
// Check if reached patrol point
if (!agent.pathPending && agent.remainingDistance < 0.5f && !isWaiting)
StartCoroutine(PatrolWait());
// Transition to chase if player spotted
if (playerSpotted && !isInvestigating)
SwitchState(AIState.Chase);
private void ChaseBehavior()
if (!playerSpotted
private void AttackBehavior()
if (isAttacking) return;
float distanceToPlayer = Vector3.Distance(transform.position, player.position);
if (distanceToPlayer <= attackRange)
StartCoroutine(PerformAttack());
else
SwitchState(AIState.Chase);
private void InvestigateBehavior()
if (!isInvestigating)
isInvestigating = true;
investigationPoint = lastKnownPlayerPosition;
agent.SetDestination(investigationPoint);
agent.speed = patrolSpeed;
// Check if reached investigation point
if (!agent.pathPending && agent.remainingDistance < 0.5f)
StartCoroutine(FinishInvestigation());
// Re-check for player during investigation
if (playerSpotted)
isInvestigating = false;
SwitchState(AIState.Chase);
private void FleeBehavior()
// Find direction away from player
Vector3 fleeDirection = transform.position - player.position;
Vector3 fleePoint = transform.position + fleeDirection.normalized * 15f;
// Ensure flee point is within navmesh
NavMeshHit hit;
if (NavMesh.SamplePosition(fleePoint, out hit, 10f, NavMesh.AllAreas))
agent.SetDestination(hit.position);
agent.speed = chaseSpeed * 1.2f;
// Regain health or return to chase
if (currentHealth > fleeHealthThreshold + 15f && playerSpotted)
SwitchState(AIState.Chase);
else if (!playerSpotted)
SwitchState(AIState.Patrol);
private void TakeCoverBehavior()
!IsCoverValid(currentCoverPoint))
FindBestCover();
if (currentCoverPoint != Vector3.zero)
agent.SetDestination(currentCoverPoint);
// Check if in cover
if (!agent.pathPending && agent.remainingDistance < 1f)
// Wait in cover
StartCoroutine(WaitInCover());
else
SwitchState(AIState.Chase);
#endregion
#region Helper Methods
private IEnumerator SensePlayerRoutine()
while (!isDead)
if (player != null && !playerSpotted)
yield return new WaitForSeconds(0.2f);
private bool CanSeePlayer()
if (player == null) return false;
Vector3 directionToPlayer = (player.position - transform.position).normalized;
float angleToPlayer = Vector3.Angle(transform.forward, directionToPlayer);
if (angleToPlayer < fieldOfView / 2)
float distanceToPlayer = Vector3.Distance(transform.position, player.position);
if (distanceToPlayer <= chaseRange)
// Raycast to check for obstacles
RaycastHit hit;
if (Physics.Raycast(transform.position + Vector3.up * 1f, directionToPlayer, out hit, chaseRange, obstructionMask))
if (hit.transform == player
return false;
private bool CanHearPlayer()
if (player == null) return false;
float distanceToPlayer = Vector3.Distance(transform.position, player.position);
// Check if player is moving fast (running)
CharacterController controller = player.GetComponent<CharacterController>();
if (controller != null && controller.velocity.magnitude > 2f)
return distanceToPlayer < hearingRadius;
return false;
private bool IsUnderFire()
// Simple check - can be expanded with projectile hit detection
return currentHealth < maxHealth * 0.7f;
private void FindBestCover()
// Simple cover finding - looks for objects with "Cover" tag
GameObject[] coverObjects = GameObject.FindGameObjectsWithTag("Cover");
float bestScore = 0f;
Vector3 bestCover = Vector3.zero;
foreach (GameObject cover in coverObjects)
Vector3 coverPos = cover.transform.position;
float distanceToCover = Vector3.Distance(transform.position, coverPos);
float distanceFromPlayer = Vector3.Distance(player.position, coverPos);
// Score based on distance and protection
float score = (100f / distanceToCover) + distanceFromPlayer;
if (score > bestScore && IsCoverValid(coverPos))
bestScore = score;
bestCover = coverPos;
currentCoverPoint = bestCover;
private bool IsCoverValid(Vector3 coverPoint)
if (player == null) return false;
// Check if cover is between enemy and player
Vector3 directionToPlayer = player.position - transform.position;
Vector3 directionToCover = coverPoint - transform.position;
float angleDiff = Vector3.Angle(directionToPlayer, directionToCover);
return angleDiff < 45f;
private IEnumerator PerformAttack()
isAttacking = true;
nextAttackTime = Time.time + attackCooldown;
// Play attack animation
if (animator != null)
animator.SetTrigger("Attack");
PlaySound(attackSounds);
// Wait for attack animation
yield return new WaitForSeconds(attackAnimDuration * 0.5f);
// Deal damage
if (player != null && Vector3.Distance(transform.position, player.position) <= attackRange)
if (isRangedAttacker && projectilePrefab != null && projectileSpawnPoint != null)
GameObject projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, Quaternion.LookRotation(player.position - projectileSpawnPoint.position));
Projectile projectileScript = projectile.GetComponent<Projectile>();
if (projectileScript != null)
projectileScript.Initialize(attackDamage, player.gameObject);
else
// Melee damage
PlayerHealth playerHealth = player.GetComponent<PlayerHealth>();
if (playerHealth != null)
playerHealth.TakeDamage(attackDamage);
yield return new WaitForSeconds(attackAnimDuration * 0.5f);
isAttacking = false;
SwitchState(AIState.Chase);
private IEnumerator PatrolWait()
isWaiting = true;
agent.isStopped = true;
yield return new WaitForSeconds(waitTimeAtPatrolPoint);
agent.isStopped = false;
SetNewPatrolPoint();
isWaiting = false;
private IEnumerator FinishInvestigation()
yield return new WaitForSeconds(2f);
isInvestigating = false;
playerSpotted = false;
SwitchState(AIState.Patrol);
private IEnumerator WaitInCover()
agent.isStopped = true;
yield return new WaitForSeconds(3f);
agent.isStopped = false;
SwitchState(AIState.Chase);
private IEnumerator CoverCheckRoutine()
while (!isDead)
nextCoverCheck = Time.time + coverCheckInterval;
yield return new WaitForSeconds(coverCheckInterval);
private void SetNewPatrolPoint()
Vector3 randomDirection = Random.insideUnitSphere * patrolRadius;
randomDirection += transform.position;
NavMeshHit hit;
if (NavMesh.SamplePosition(randomDirection, out hit, patrolRadius, NavMesh.AllAreas))
patrolTarget = hit.position;
agent.SetDestination(patrolTarget);
private void SwitchState(AIState newState)
if (currentState == newState) return;
// Exit current state
switch (currentState)
case AIState.Attack:
StopCoroutine(PerformAttack());
isAttacking = false;
break;
currentState = newState;
// Enter new state
switch (currentState)
case AIState.Patrol:
agent.speed = patrolSpeed;
break;
case AIState.Flee:
if (animator != null)
animator.SetTrigger("Flee");
break;
private void UpdateAnimations()
if (animator == null) return;
float speed = agent.velocity.magnitude;
animator.SetFloat("Speed", speed);
animator.SetBool("IsDead", isDead);
private void PlaySound(AudioClip[] clips)
clips.Length == 0) return;
AudioClip clip = clips[Random.Range(0, clips.Length)];
audioSource.PlayOneShot(clip, audioVolume);
#endregion
#region Public Methods
public void TakeDamage(int damage)
if (isDead) return;
currentHealth -= damage;
PlaySound(hurtSounds);
if (animator != null)
animator.SetTrigger("Hurt");
if (currentHealth <= 0)
Die();
else
// React to being hit
playerSpotted = true;
lastKnownPlayerPosition = player.position;
SwitchState(AIState.Chase);
private void Die()
isDead = true;
agent.isStopped = true;
PlaySound(deathSounds);
if (animator != null)
animator.SetTrigger("Die");
// Disable collider to prevent further interaction
Collider col = GetComponent<Collider>();
if (col != null)
col.enabled = false;
Destroy(gameObject, 5f);
#endregion
// Optional: Projectile script for ranged attackers
public class Projectile : MonoBehaviour
private int damage;
private GameObject ignoreTarget;
[SerializeField] private float speed = 20f;
[SerializeField] private float lifetime = 5f;
public void Initialize(int dmg, GameObject ignore)
damage = dmg;
ignoreTarget = ignore;
Destroy(gameObject, lifetime);
void Update()
transform.Translate(Vector3.forward * speed * Time.deltaTime);
void OnTriggerEnter(Collider other)
if (other.gameObject == ignoreTarget) return;
if (other.CompareTag("Player"))
PlayerHealth playerHealth = other.GetComponent<PlayerHealth>();
if (playerHealth != null)
playerHealth.TakeDamage(damage);
Destroy(gameObject);
Script Structure:
The structure of such a script would depend heavily on the platform (e.g., Unity, Unreal Engine) and the programming languages used (e.g., C#, C++, Blueprints).
For example, in Unity with C#, a simple example of a mechanic could be: $$void opponentMovement()$$ $$$$ $$ // Define movement speed$$ $$ public float speed = 5.0f;$$ $$ // Reference to the opponent's transform$$ $$ private Transform opponent;$$ $$ // Update is called once per frame$$ $$ void Update()$$ $$ $$ $$ // Simple movement$$ $$ opponent.position += new Vector3(0, 0, speed * Time.deltaTime);$$ $$ $$ $$ $$ opposer vr script
The Archer (Ranged Opposer)
- Modification: Remove dodge logic; add projectile prediction.
- Special Logic: The script uses
Vector3.Lerpto lead its shots. It aims not at the player's current head, but where the head will be in 0.2 seconds.
2.1 Reactive Blocking (if player raises hand)
public class VRBlockReaction : MonoBehaviour public VROpposer opposer; public Transform leftHand, rightHand; public float blockDetectionAngle = 45f;void Update() bool isBlocking = CheckIfPlayerIsBlocking(); opposer.animator.SetBool("PlayerBlocking", isBlocking); if (isBlocking && opposer.enabled) opposer.attackCooldown *= 0.7f; // shorter cooldown if blocked bool CheckIfPlayerIsBlocking()



Post Comment