オンラインFPS:キルデスイベント

+ボタン押下でコードを確認できます!

 

コピペしても正しく動かない場合は
エディタでアタッチし忘れている可能性が高いです。

PlayerController
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
public class PlayerController : MonoBehaviourPunCallbacks
{
public Transform viewPoint;//カメラの位置オブジェクト
public float mouseSensitivity = 1f;//視点移動の速度
private Vector2 mouseInput;//ユーザーのマウス入力を格納
private float verticalMouseInput;//y軸の回転を格納 回転を制限したいから
private Camera cam;//カメラ
private Vector3 moveDir;//プレイヤーの入力を格納(移動)
private Vector3 movement;//進む方向を格納する変数
private float activeMoveSpeed = 4;//実際の移動速度
public Vector3 jumpForce = new Vector3(0, 6, 0);//ジャンプ力
public Transform groundCheckPoint;//地面に向けてレイを飛ばすオブジェクト
public LayerMask groundLayers;//地面だと認識するレイヤー
Rigidbody rb;//
public float walkSpeed = 4f, runSpeed = 8f;//歩きの速度、走りの速度
private bool cursorLock = true;//カーソルの表示/非表示
public List<Gun> guns = new List<Gun>();//武器の格納配列
private int selectedGun = 0;//選択中の武器管理用数値
private float shotTimer;//射撃間隔
[Tooltip("所持弾薬")]
public int[] ammunition;
[Tooltip("最高所持弾薬数")]
public int[] maxAmmunition;
[Tooltip("マガジン内の弾数")]
public int[] ammoClip;
[Tooltip("マガジンに入る最大の数")]
public int[] maxAmmoClip;
UIManager uIManager;//UI管理
SpawnManager spawnManager;//スポーンマネージャー管理
public Animator animator;//アニメーター
public GameObject[] playerModel;//プレイヤーモデルを格納
public Gun[] gunsHolder, OtherGunsHolder;//銃ホルダー
public int maxHP = 100;//最大HP
private int currentHp;//現在のHP
public GameObject hitEffect;//血のエフェクト
GameManager gameManager;//ゲームマネージャー
private void Awake()
{
//タグからUIManagerを探す
uIManager = GameObject.FindGameObjectWithTag("UIManager").GetComponent<UIManager>();
//タグからSpawnManagerを探す
spawnManager = GameObject.FindGameObjectWithTag("SpawnManager").GetComponent<SpawnManager>();
//タグからGameManagerを探す
gameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
}
private void Start()
{
currentHp = maxHP;//現在のHPをMAXHPの数値に設定
//変数にメインカメラを格納
cam = Camera.main;
//Rigidbodyを格納
rb = GetComponent<Rigidbody>();
//カーソル非表示
UpdateCursorLock();
//弾薬テキスト更新
uIManager.SettingBulletsText(ammoClip[selectedGun], ammunition[selectedGun]);
//ランダムな位置でスポーンさせる
//transform.position = spawnManager.GetSpawnPoint().position;
guns.Clear();//初期化
if (photonView.IsMine)//自分だったら
{
foreach (var model in playerModel)//モデルのパーツ分ループ
{
model.SetActive(false);//非表示
}
foreach (Gun gun in gunsHolder)//銃の数分ループ
{
guns.Add(gun);//リストに追加
}
uIManager.UpdateHP(maxHP, currentHp);//HPをスライダーに反映
}
else//他人だったらOtherGunsHolderを表示させる
{
foreach (Gun gun in OtherGunsHolder)//銃の数分ループ
{
guns.Add(gun);//リストに追加
}
}
switchGun();//銃を表示させるため
}
private void Update()
{
//自分以外は
if (!photonView.IsMine)
{
//戻ってこれ以降の処理を行わない
return;
}
//視点移動関数
PlayerRotate();
//移動関数
PlayerMove();
//地面についているのか判定をする
if (IsGround())
{
//走りの関数を呼ぶ
Run();
//ジャンプ関数を呼ぶ
Jump();
}
//銃の切り替え
SwitchingGuns();
//覗き込み
Aim();
//射撃関数
Fire();
//リロード関数
Reload();
//カーソル非表示
UpdateCursorLock();
//アニメーター遷移
AnimatorSet();
}
//Update関数が呼ばれた後に実行される
private void LateUpdate()
{
//自分以外は
if (!photonView.IsMine)
{
//戻ってこれ以降の処理を行わない
return;
}
//カメラをプレイヤーの子にするのではなく、スクリプトで位置を合わせる
cam.transform.position = viewPoint.position;
cam.transform.rotation = viewPoint.rotation;
}
//初期設定では0.02秒ごとに呼ばれる
private void FixedUpdate()
{
//自分以外は
if (!photonView.IsMine)
{
//戻ってこれ以降の処理を行わない
return;
}
//弾薬テキスト更新
uIManager.SettingBulletsText(ammoClip[selectedGun], ammunition[selectedGun]);
}
/// <summary>
/// Playerの横回転と縦の視点移動を行う
/// </summary>
public void PlayerRotate()
{
//変数にユーザーのマウスの動きを格納
mouseInput = new Vector2(Input.GetAxisRaw("Mouse X") * mouseSensitivity,
Input.GetAxisRaw("Mouse Y") * mouseSensitivity);
//横回転を反映(transform.eulerAnglesはオイラー角としての角度が返される)
transform.rotation = Quaternion.Euler
(transform.eulerAngles.x,
transform.eulerAngles.y + mouseInput.x, //マウスのx軸の入力を足す
transform.eulerAngles.z);
//変数にy軸のマウス入力分の数値を足す
verticalMouseInput += mouseInput.y;
//変数の数値を丸める(上下の視点制御)
verticalMouseInput = Mathf.Clamp(verticalMouseInput, -60f, 60f);
//縦の視点回転を反映(-を付けないと上下反転してしまう)
viewPoint.rotation = Quaternion.Euler
(-verticalMouseInput,
viewPoint.transform.rotation.eulerAngles.y,
viewPoint.transform.rotation.eulerAngles.z);
}
/// <summary>
/// Playerの移動
/// </summary>
public void PlayerMove()
{
//変数の水平と垂直の入力を格納する(wasdや矢印の入力)
moveDir = new Vector3(Input.GetAxisRaw("Horizontal"),
0, Input.GetAxisRaw("Vertical"));
//Debug.Log(moveDir);説明用
//ゲームオブジェクトのz軸とx軸に入力された値をかけると進む方向が出せる
movement = ((transform.forward * moveDir.z) + (transform.right * moveDir.x)).normalized;
//現在位置に進む方向*移動スピード*フレーム間秒数を足す
transform.position += movement * activeMoveSpeed * Time.deltaTime;
}
/// <summary>
/// 地面についているならtrue
/// </summary>
/// <returns></returns>
public bool IsGround()
{
return Physics.Raycast(groundCheckPoint.position, Vector3.down, 0.25f, groundLayers);
}
public void Jump()
{
//ジャンプできるのか判定
if (IsGround() && Input.GetKeyDown(KeyCode.Space))
{
//瞬間的に真上に力を加える
rb.AddForce(jumpForce, ForceMode.Impulse);
}
}
public void Run()
{
//左シフトを押しているときはスピードを切り替える
if(Input.GetKey(KeyCode.LeftShift))
{
activeMoveSpeed = runSpeed;
}
else
{
activeMoveSpeed = walkSpeed;
}
}
public void UpdateCursorLock()
{
//入力しだいでcursorLockを切り替える
if (Input.GetKeyDown(KeyCode.Escape))
{
cursorLock = false;
}
else if (Input.GetMouseButton(0))
{
cursorLock = true;
}
//cursorLock次第でカーソルの表示を切り替える
if (cursorLock)
{
//カーソルを中央に固定し、非表示 https://docs.unity3d.com/ScriptReference/CursorLockMode.html
Cursor.lockState = CursorLockMode.Locked;
}
else
{
//表示
Cursor.lockState = CursorLockMode.None;
}
}
/// <summary>
/// 銃の切り替えキー入力を検知する
/// </summary>
public void SwitchingGuns()
{
if (Input.GetAxisRaw("Mouse ScrollWheel") > 0f)
{
selectedGun++;//扱う銃を管理する数値を増やす
//リストより大きい数値になっていないか確認
if (selectedGun >= guns.Count)
{
selectedGun = 0;//リストより大きな数値になれば0に戻す
}
//実際に武器を切り替える関数
//switchGun();
//銃の切り替え(ルーム内のプレイヤー全員呼び出し)
photonView.RPC("SetGun", RpcTarget.All, selectedGun);
}
else if (Input.GetAxisRaw("Mouse ScrollWheel") < 0f)
{
selectedGun--;//扱う銃を管理する数値を減らす
if (selectedGun < 0)
{
selectedGun = guns.Count - 1;//0より小さければリストの最大数-1の数値に設定する
}
//実際に武器を切り替える関数
//switchGun();
//銃の切り替え(ルーム内のプレイヤー全員呼び出し)
photonView.RPC("SetGun", RpcTarget.All, selectedGun);
}
//数値キーの入力検知で武器を切り替える
for (int i = 0; i < guns.Count; i++)
{
if (Input.GetKeyDown((i + 1).ToString()))//ループの数値+1をして文字列に変換。その後、押されたか判定
{
selectedGun = i;//銃を扱う数値を設定
//実際に武器を切り替える関数
//switchGun();
//銃の切り替え(ルーム内のプレイヤー全員呼び出し)
photonView.RPC("SetGun", RpcTarget.All, selectedGun);
}
}
}
/// <summary>
/// 銃の切り替え
/// </summary>
void switchGun()
{
foreach (Gun gun in guns)//リスト分ループを回す
{
gun.gameObject.SetActive(false);//銃を非表示
}
guns[selectedGun].gameObject.SetActive(true);//選択中の銃のみ表示
}
/// <summary>
/// 右クリックで覗き込み
/// </summary>
public void Aim()
{
// マウス右ボタン押しているとき
if (Input.GetMouseButton(1))
{
//fieldOfViewコンポーネントの値を変更(開始地点、目的地点、補完数値)  開始地点から目的地点まで補完数値の割合で徐々に近づける
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, guns[selectedGun].adsZoom, guns[selectedGun].adsSpeed * Time.deltaTime);
}
else
{ //60は初期設定数値
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, 60f, guns[selectedGun].adsSpeed * Time.deltaTime);
}
}
/// <summary>
/// 左クリックの検知
/// </summary>
public void Fire()
{
if (Input.GetMouseButton(0) && ammoClip[selectedGun] > 0 && Time.time > shotTimer)
{
FiringBullet();
}
}
/// <summary>
/// 弾丸の発射
/// </summary>
private void FiringBullet()
{
//選択中の銃の弾薬減らす
ammoClip[selectedGun]--;
//Ray(光線)をカメラの中央からに設定
Ray ray = cam.ViewportPointToRay(new Vector2(.5f, .5f));//カメラの中心がこの数値
//レイを飛ばす(開始地点と方向、当たったコライダーの情報格納)
if (Physics.Raycast(ray, out RaycastHit hit))
{
//Debug.Log("当たったオブジェクトは" + hit.collider.gameObject.name);
if (hit.collider.gameObject.tag == "Player")//プレイヤーにぶつかった場合
{
//血のエフェクトをネットワーク上に生成
PhotonNetwork.Instantiate(hitEffect.name, hit.point, Quaternion.identity);
// ヒット関数を全プレイヤーで呼び出して撃たれたプレイヤーのHPを同期する
hit.collider.gameObject.GetPhotonView().RPC("Hit", RpcTarget.All, guns[selectedGun].shotDamage, photonView.Owner.NickName, PhotonNetwork.LocalPlayer.ActorNumber);
}
else
{
//弾痕エフェクト (hit.pointはコライダーにヒットした位置):hit.point + (hit.normal * .002f)はちらつかないように少し上にしている
//hit normalは当たったオブジェクトに対して直角の方向が返される
//LookRotationは指定した方向に回す
GameObject bulletImpactObject = Instantiate(guns[selectedGun].bulletImpact, hit.point + (hit.normal * .002f), Quaternion.LookRotation(hit.normal, Vector3.up));
//時間経過で消えるようにする
Destroy(bulletImpactObject, 10f);
}
}
//射撃間隔を設定
shotTimer = Time.time + guns[selectedGun].shootInterval;
}
/// <summary>
/// リロード
/// </summary>
private void Reload()
{
if (Input.GetKeyDown(KeyCode.R))
{
//リロードで補充する弾数を取得する
int amountNeed = maxAmmoClip[selectedGun] - ammoClip[selectedGun];
//必要な弾薬量と所持弾薬量を比較
int ammoAvailable = amountNeed < ammunition[selectedGun] ? amountNeed : ammunition[selectedGun];
//弾薬が満タンの時はリロードできない&弾薬を所持しているとき
if (amountNeed != 0 && ammunition[selectedGun] != 0)
{
//所持弾薬からリロードする弾薬分を引く
ammunition[selectedGun] -= ammoAvailable;
//銃に装填する
ammoClip[selectedGun] += ammoAvailable;
}
}
}
private void AnimatorSet()
{
//walk判定
if (moveDir != Vector3.zero)
{
animator.SetBool("walk", true);
}
else
{
animator.SetBool("walk", false);
}
//run判定
if (Input.GetKey(KeyCode.LeftShift))
{
animator.SetBool("run", true);
}
else
{
animator.SetBool("run", false);
}
}
[PunRPC]//(同じルームにいる)リモートクライアントに対してのメソッドの呼び出しが可能に
public void SetGun(int gunNo)
{
//銃の切り替え
if (gunNo < guns.Count)
{
selectedGun = gunNo;
switchGun();
}
}
/// <summary>
/// 敵の弾に当たったら呼ばれる関数(全プレイヤーで共有するためPunRPC)
/// </summary>
[PunRPC]
public void Hit(int damage, string name, int actor)//ダメージ、撃った奴の名前、撃った奴の番号
{
ReceiveDamage(name, damage, actor);//ダメージ関数呼び出し
}
/// <summary>
/// ダメージを受ける関数
/// </summary>
public void ReceiveDamage(string name, int damage, int actor)
{
if (photonView.IsMine)//自分なら
{
currentHp -= damage;//ダメージ
if (currentHp <= 0)//現在のHPが0以下の場合
{
Death(name, actor);//死亡関数を呼ぶ
}
uIManager.UpdateHP(maxHP, currentHp);//HPをスライダーに反映
}
}
//死亡処理
public void Death(string name, int actor)
{
currentHp = 0;
//死亡関数を呼ぶ
spawnManager.Die();
uIManager.UpdateDeathUI(name);
//自分のデス数を上昇させる(自分の識別番号、デス、加算数値)
gameManager.ScoreGet(PhotonNetwork.LocalPlayer.ActorNumber, 1, 1);
//撃ってきた相手のキル数を上昇する(撃ってきた敵の識別番号、キル、加算数値)
gameManager.ScoreGet(actor, 0, 1);
}
}

 

GameManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using UnityEngine.SceneManagement;
using Photon.Realtime;
using ExitGames.Client.Photon;
//リアルタイムAPIのイベントコールバック。サーバーからのイベントと、OpRaiseEventを介してクライアントから送信されたイベントをカバーします。
//カスタムイベントを受信することができるようになる
public class GameManager : MonoBehaviourPunCallbacks, IOnEventCallback
{
//イベントについて記載があるドキュメント
//https://doc.photonengine.com/ja-jp/pun/current/gameplay/rpcsandraiseevent#_thy2n6w3gsafi04
public List<PlayerInfo> playerList = new List<PlayerInfo>();//プレイヤー情報を扱うクラスのリスト
public enum EventCodes : byte//自作イベント:byteは扱うデータ(0 ~ 255)
{
NewPlayer,//新しいプレイヤー情報をマスターに送る
ListPlayers,//全員にプレイヤー情報を共有
UpdateStat,//キルデス数の更新
}
/// <summary>
/// ゲームの状態
/// </summary>
public enum GameState
{
Playing,
Ending
}
public GameState state;//状態を格納
private void Start()
{
//ネットワーク接続されていない場合
if (!PhotonNetwork.IsConnected)
{
//タイトルに戻る
SceneManager.LoadScene(0);
}
else//繋がっている場合
{
//マスターにユーザー情報を発信する
NewPlayerGet(PhotonNetwork.NickName);
//状態をゲーム中に設定する
state = GameState.Playing;
}
}
//イベント発生時に呼び出される
public void OnEvent(EventData photonEvent)
{
if (photonEvent.Code < 200)//200以上はphotonが独自のイベントを使っているため200以下のみに処理をする
{
EventCodes eventCode = (EventCodes)photonEvent.Code;//今回のイベントコードを格納(型変換)
object[] data = (object[])photonEvent.CustomData;//インデクサーとCustomDataKeyを介して、イベントのカスタムデータにアクセスします
switch (eventCode)
{
case EventCodes.NewPlayer://発生したイベントがNewPlayerなら
NewPlayerSet(data);//マスターに新規ユーザー情報処理させる
break;
case EventCodes.ListPlayers:
ListPlayersSet(data);//ユーザー情報を共有
break;
case EventCodes.UpdateStat:
ScoreSet(data);//
break;
}
}
}
public override void OnEnable()//コンポーネントがオンになると呼ばれる
{
//実装されているコールバック・インターフェースのコールバック用オブジェクトを登録します。
PhotonNetwork.AddCallbackTarget(this);//追加する
}
public override void OnDisable()//コンポーネントがオフになると呼ばれる
{
PhotonNetwork.RemoveCallbackTarget(this);//削除する
}
/// <summary>
/// 新規ユーザーがネットワーク経由でマスターに自分の情報を送る
/// </summary>
public void NewPlayerGet(string name)//イベントを発生させる関数
{
//objectは色々な型を入れることができる:
object[] info = new object[4];//データ格納配列を作成
info[0] = name;//名前
info[1] = PhotonNetwork.LocalPlayer.ActorNumber;//ユーザー管理番号
info[2] = 0;//キル
info[3] = 0;//デス
// RaiseEventでカスタムイベントを発生:データを送る
PhotonNetwork.RaiseEvent((byte)EventCodes.NewPlayer,//発生させるイベント
info,//送るもの(プレイヤーデータ)
new RaiseEventOptions { Receivers = ReceiverGroup.MasterClient },//ルームマスターだけに送信される設定
new SendOptions { Reliability = true }//信頼性の設定:信頼できるのでプレイヤーに送信される
);
}
/// <summary>
///送られてきた新プレイヤーの情報をリストに格納する
/// </summary>
public void NewPlayerSet(object[] data)//マスターが行う処理 イベント発生時に行う処理
{
PlayerInfo player = new PlayerInfo((string)data[0], (int)data[1], (int)data[2], (int)data[3]);//ネットワークからプレイヤー情報を取得
playerList.Add(player);//リストに追加
ListPlayersGet();//マスターが取得したプレイヤー情報を他のプレイヤーに共有
}
/// <summary>
/// 取得したプレイヤー情報をルーム内の全プレイヤーに送信する
/// </summary>
public void ListPlayersGet()//マスターが行う処理 イベントを発生させる関数
{
object[] info = new object[playerList.Count + 1];//ゲームの状況&プレイヤー情報格納配列作成
info[0] = state;//最初にはゲームの状況を入れる
for (int i = 0; i < playerList.Count; i++) //参加ユーザーの数分ループ
{
object[] temp = new object[4];//一時的格納する配列
temp[0] = playerList[i].name;
temp[1] = playerList[i].actor;
temp[2] = playerList[i].kills;
temp[3] = playerList[i].deaths;
info[i + 1] = temp;//プレイヤー情報を格納している配列に格納する。0にはゲームの状態が入っているため+1する。
}
//RaiseEventでカスタムイベントを発生:データを送る
PhotonNetwork.RaiseEvent((byte)EventCodes.ListPlayers,////発生させるイベント
info,//送るもの(プレイヤーデータ)
new RaiseEventOptions { Receivers = ReceiverGroup.All },//全員に送信するイベント設定
new SendOptions { Reliability = true }//信頼性の設定:信頼できるのでプレイヤーに送信される
);
}
/// <summary>
/// ListPlayersSendで新しくプレイヤー情報が送られてきたので、リストに格納する
/// </summary>
public void ListPlayersSet(object[] data)//イベントが発生したら呼ばれる関数 全プレイヤーで呼ばれる
{
playerList.Clear();//既に持っているプレイヤーのリストを初期化
state = (GameState)data[0];//ゲーム状態を変数に格納
for (int i = 1; i < data.Length; i++)//1にする 0はゲーム状態なので1から始める
{
object[] info = (object[])data[i];//
PlayerInfo player = new PlayerInfo(
(string)info[0],//名前
(int)info[1],//管理番号
(int)info[2],//キル
(int)info[3]);//デス
playerList.Add(player);//リストに追加
}
}
/// <summary>
/// キル数やデス数を取得する関数(プレイヤー識別数、キルかデスを数値で判定、加算する数値)
/// </summary>
public void ScoreGet(int actor, int stat, int amount)
{
object[] package = new object[] { actor, stat, amount };
//データを送るイベント
PhotonNetwork.RaiseEvent((byte)EventCodes.UpdateStat,//発生させるイベント
package,//送るもの(プレイヤーのキルデスデータ)
new RaiseEventOptions { Receivers = ReceiverGroup.All },//全員に送信するイベント設定
new SendOptions { Reliability = true }//信頼性の設定:信頼できるのでプレイヤーに送信される
);
}
/// <summary>
/// 受け取ったデータからリストにキルデス情報を追加
/// </summary>
public void ScoreSet(object[] data)
{
int actor = (int)data[0];//識別数
int stat = (int)data[1];//キルなのかデスなのか
int amount = (int)data[2];//加算する数値
for (int i = 0; i < playerList.Count; i++)//プレイヤーの数分ループ
{
if (playerList[i].actor == actor)//情報を取得したプレイヤーと数値が合致したとき
{
switch (stat)
{
case 0://キル
playerList[i].kills += amount;
break;
case 1://デス
playerList[i].deaths += amount;
break;
}
break;//処理はできたのでこれ以降for文を回さないためにブレイクする
}
}
}
}
[System.Serializable]
public class PlayerInfo//プレイヤー情報を管理するクラス
{
public string name;//名前
public int actor, kills, deaths;//番号、キル、デス
//情報を格納
public PlayerInfo(string _name, int _actor, int _kills, int _death)
{
name = _name;
actor = _actor;
kills = _kills;
deaths = _death;
}
}
view raw GameManager.cs hosted with ❤ by GitHub

 

他の講座クーポン【93%OFF】

関連記事

クーポン期限2023/01/24 Udemyでは定価24000円になっていますがこのクーポンページからの購入で1800円台で購入することが可能になっています! 30日以内な[…]

unity fps

 

Udemyよりお得に講座視聴可能なYouTubeメンバーシップ