Introduction:
In this update, we'll focus on integrating weapons and shooting mechanics into our game. Weapons play a crucial role in determining player effectiveness and shaping gameplay dynamics. By implementing raycasting and damage logic, we'll enable players to engage in combat and interact with the game world effectively.
Item Hierarchy:
Before diving into weapon mechanics, let's establish a hierarchy for items in our game:
Item (Abstract Class): Represents a generic item in the game, providing a blueprint for specific item types.
using System.Collections; using System.Collections.Generic; using UnityEngine; public abstract class Item : MonoBehaviour { public ItemInfo itemInfo; public GameObject itemGameObject; public abstract void Use(); }
Gun (Abstract Class): Extends the Item class and serves as the base class for all firearms in the game.
using System.Collections; using System.Collections.Generic; using UnityEngine; public abstract class Gun : Item { public override abstract void Use(); }
Knife (Abstract Class): Extends the Item class and represents melee weapons, such as combat knives.
using System.Collections; using System.Collections.Generic; using UnityEngine; public abstract class Knife : Item { public override abstract void Use(); }
Weapon Attributes:
Each weapon in the game is defined by its unique attributes, such as damage output and range. These attributes are encapsulated within respective ScriptableObjects:
GunInfo (ScriptableObject): Stores information specific to firearms, including damage dealt per shot.
KnifeInfo (ScriptableObject): Stores information for melee weapons, such as damage inflicted on contact.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "FPS/New Gun")]
public class GunInfo : ItemInfo
{
public float damage;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemInfo : ScriptableObject
{
public string ItemName;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "FPS/New Knife")]
public class KnifeInfo : ItemInfo
{
public float damage;
}
Weapon Scripts:
Now, let's explore the implementation of various weapon scripts, each responsible for handling specific types of weapons:
SingleShotGun: Represents a firearm capable of firing single shots. Uses raycasting to detect hits and deal damage to targets.
using Photon.Pun; using System.Collections; using System.Collections.Generic; using UnityEngine; public class SingleShotGun : Gun { [SerializeField] Camera cam; public override void Use() { Shoot(); } public void Shoot() { Ray ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f)); ray.origin = cam.transform.position; if(Physics.Raycast(ray, out RaycastHit hitInfo)) { PhotonView hitPhotonView = hitInfo.collider.gameObject.GetComponent<PhotonView>(); if ((hitPhotonView != null)) { string shooterTeam = (string)PhotonNetwork.LocalPlayer.CustomProperties["Team"]; string hitPlayerTeam = (string)hitPhotonView.Owner?.CustomProperties["Team"]; if(hitPlayerTeam != shooterTeam) { Debug.Log("Hit on: " + hitInfo.collider.gameObject.name); hitInfo.collider.gameObject.GetComponent<IDamageable>()?.TakeDamage(((GunInfo)itemInfo).damage); } } } } }
CombatKnife: Represents a melee weapon, allowing players to perform close-range attacks. Utilizes raycasting to determine hit targets and inflict damage.
using Photon.Pun; using System.Collections; using System.Collections.Generic; using UnityEngine; public class CombatKnife : Knife { [SerializeField] Camera cam; [SerializeField] float maxDistance = 2f; public override void Use() { Kill(); } public void Kill() { Ray ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f)); ray.origin = cam.transform.position; if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance)) { PhotonView hitPhotonView = hitInfo.collider.gameObject.GetComponent<PhotonView>(); if ((hitPhotonView != null)) { string shooterTeam = (string)PhotonNetwork.LocalPlayer.CustomProperties["Team"]; string hitPlayerTeam = (string)hitPhotonView.Owner?.CustomProperties["Team"]; if (hitPlayerTeam != shooterTeam) { Debug.Log("Hit on: " + hitInfo.collider.gameObject.name); hitInfo.collider.gameObject.GetComponent<IDamageable>()?.TakeDamage(((KnifeInfo)itemInfo).damage); } } } } }
GrenadeGun: Simulates a grenade launcher capable of firing explosive projectiles. Creates explosion volumes upon impact to damage nearby entities.
using Photon.Pun; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; public class GrenadeGun : Gun { [SerializeField] Camera cam; private float explosionRadius = 5f; private float explosionDelay = 2f; //[SerializeField] GameObject ExplosionVolume; Vector3 targetPosition; GameObject _explosionVolume; public override void Use() { GrenadeDamage(); } public void GrenadeDamage() { Ray ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f)); ray.origin = cam.transform.position; if (Physics.Raycast(ray, out RaycastHit hitInfo)) { Debug.Log("Launcher hit on: " + hitInfo.collider.gameObject.name); targetPosition = hitInfo.point; GameObject explosionVolume = PhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", "GrenadeArea"), targetPosition, Quaternion.identity); _explosionVolume = explosionVolume; // Invoke the explosion after the delay Invoke("Explode", explosionDelay); } } private void Explode() { PhotonNetwork.Destroy(_explosionVolume); // Find all colliders in the explosion radius Collider[] colliders = Physics.OverlapSphere(targetPosition, explosionRadius); // Loop through each collider foreach (Collider col in colliders) { col.gameObject.GetComponent<IDamageable>()?.TakeDamage(((GunInfo)itemInfo).damage); } } }
RadiusDamageGun: Represents a launcher weapon with area-of-effect damage capabilities. Generates explosion volumes to inflict damage within a specified radius.
```csharp using Photon.Pun; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine;
public class RaidusDamageGun : Gun { [SerializeField] Camera cam; private float explosionRadius = 5f; private float explosionDelay = 1f;
//[SerializeField] GameObject ExplosionVolume; Vector3 targetPosition;
GameObject _explosionVolume;
public override void Use() { RadiusDamage(); }
public void RadiusDamage() { Ray ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f)); ray.origin = cam.transform.position; if (Physics.Raycast(ray, out RaycastHit hitInfo)) { Debug.Log("Launcher hit on: " + hitInfo.collider.gameObject.name); targetPosition = hitInfo.point;
GameObject explosionVolume = PhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", "LauncherArea") , targetPosition, Quaternion.identity);
_explosionVolume = explosionVolume;
//PhotonNetwork.Destroy(explosionVolume);
// Invoke the explosion after the delay Invoke("Explode", explosionDelay); } }
private void Explode() { PhotonNetwork.Destroy(_explosionVolume); // Find all colliders in the explosion radius Collider[] colliders = Physics.OverlapSphere(targetPosition, explosionRadius);
// Loop through each collider foreach (Collider col in colliders) { col.gameObject.GetComponent()?.TakeDamage(((GunInfo)itemInfo).damage); }
} } ```
Conclusion:
With the integration of weapon mechanics and shooting functionalities, our game takes a significant step forward in delivering engaging combat experiences. Players can now wield various firearms and melee weapons, each offering distinct advantages and playstyles. By balancing weapon attributes and behavior, we ensure fair and enjoyable gameplay for all participants. In the next update, we'll explore further enhancements, including player feedback mechanisms and visual effects for weapon interactions.