オンラインFPS:キルデスイベント
+ボタン押下でコードを確認できます!
コピペしても正しく動かない場合は
エディタでアタッチし忘れている可能性が高いです。
- PlayerController
- This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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
- This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
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; } }
他の講座クーポン【93%OFF】
関連記事
クーポン期限2023/01/24 Udemyでは定価24000円になっていますがこのクーポンページからの購入で1800円台で購入することが可能になっています! 30日以内な[…]
Udemyよりお得に講座視聴可能なYouTubeメンバーシップ