游玩开发之路总(二):关于第一人称射击游戏开发实战小结

   关于第一人称射击的玩耍,想起了自己玩过的率先缓第一人称射击网游,当时充分火热的《反恐精英Online》,当时啊是戏这个游乐之上只能算得感觉好游戏才去玩,现在再也玩起这种第一人称射击类游戏的时候,可以说基本上矣一个目的,带在玩的目的去玩,或者说我重新眷恋了解里面的原理,也就是说这个动作要这种效应是怎落实的,做打开发之不仅仅要了解其然还要知其所以然,我思念带在这么同样栽的学习态度我们才会以上学的长河中拿走更多之知识,这同沾非常重大。

本周是诺基亚
150周年,最近游人如织和诺基亚经游戏贪食蛇(Snake)相关的一日游啊还伪造了出。
如此这般一舒缓简单的游乐,有什么特别之处让大家这么热衷呢?我们总如下:

    在操演做这第一人称设计游戏的经过中法到了成百上千的事物,其中为发友好不足之地方,下面用当开是娱乐之经过中遇见的题目以及法到之事物记录下来。

  1. 对象一目了然:为了吃上因。
  2. 上报及时:吃少目标就就变长。
  3. 平整简单:在细微的屏幕里,控制左右势即可。

  (一)首先由玩家类来说

  于这练习着,玩家类是一个比根本类,里面定义了玩家的有些为主的数目,注意这里定义变量的经过中如大变量或者措施以此后的出被而调用的话,最好设置为共有的变量,这样即便无见面发得不交之状态了,记得在学习c++课程的时节,老师时说如就此面向对象的思维去编程,这里自己想得将用到之变量设置为性,例如将playerLife玩家的生命值设置为民用的变量,可以由此艺术的计来初始化或者转这生命值,还有即使是经性能的不二法门,例如public
static Life{get{return playerLife};set{playerLife=value} },这样的事例

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour
{


    //玩家的transform
    public Transform playerTraform;
    //获取玩家身上的charactercontrol组件
    CharacterController playerCC;
    //定义玩家的移动速度
    public float playerSpeed=5f;
    //定义玩家的生命值
    public float playerLife = 100f;

    //定义玩家的重力,当玩家通过控制器跳起来的时候
    public float playerGravity = 2f;
    /// <summary>
    /// 用于获取摄像机的变量
    /// 
    /// </summary>
    //摄像机的tranform
    Transform camTransform;
    //摄像机的旋转
    Vector3 camRot;
    //摄像机的高度
    float camHeight = 0.8f;


    ///
    //射击部分变量
    //枪口tranform
    Transform shootPoint;
    //射击射到的碰撞层
    public LayerMask shootMask;
    //射击射中后的粒子效果
    public Transform shootFX;
    //射击De 音效
    AudioSource shootMusic;
    public AudioClip shootClip;

    //射击的间隔时间
    float shootTime = 0f;

    //子弹伤害
    public int shootPower = 1;


    void Start()
    {
        //获取组件
        playerTraform = this.transform;

        playerCC = gameObject.GetComponent<CharacterController>();

        //获取摄像机
        camTransform = Camera.main.transform;
        Vector3 pos = playerTraform.position;
        pos.y += camHeight;
        camTransform.position = pos;

        //设置摄像机的旋转,与玩家的角度一致
        camTransform.rotation = playerTraform.rotation;
        camRot = camTransform.eulerAngles;
        //锁定鼠标
        Cursor.lockState = CursorLockMode.Locked;

        //获取音乐
        shootMusic = this.gameObject.GetComponent<AudioSource>();

        //获取枪口位置
        shootPoint = Camera.main.transform.FindChild("M16/weapon/muzzlepoint").transform;


        //初始化玩家的
        GameManager.Instance.SetLife((int)playerLife);


    }

    void Update()
    {
        //判断生命值是否为0
        if(playerLife<=0)
        {
            return;
        }

        //执行Control;
        Control();


        //射击部分的代码
        //

        //更新射击的时间
        shootTime -= Time.deltaTime;
        if(Input.GetMouseButton(0)&&shootTime<=0)
        {
            shootTime = 0.1f;
            //播放射击的音效
            shootMusic.PlayOneShot(shootClip);

            //减少弹药数量
            GameManager.Instance.SetAmout(1);

            //用Raycast用来做检测
            RaycastHit hit;

            //从枪口的位置,point点
            Ray ray=new Ray(shootPoint.position,camTransform.TransformDirection(Vector3.forward));
            if(Physics.Raycast(ray,out hit,1000,shootMask))
            {
                //如果射中
                if(hit.transform.tag.CompareTo("Enemy")==0)
                {
                    Enemy enemy = hit.transform.GetComponent<Enemy>();
                    //减少敌人的生命值
                    enemy.OnDamage(shootPower);
                }

                Instantiate(shootFX, hit.point, hit.transform.rotation);
            }

        }
    }


    /// <summary>
    /// 控制主角的移动
    /// </summary>
    void Control()
    {
        //获取鼠标的移动来进行方向的切换
        float h = Input.GetAxis("Mouse X");
        float v = Input.GetAxis("Mouse Y");

        //旋转摄像机设置
        camRot.x -= v;
        camRot.y += h;
        camTransform.eulerAngles = camRot;

        //设置主角的朝向与摄像机一致
        Vector3 playerRot = camTransform.eulerAngles;
        playerRot.x = 0;
        playerRot.z = 0;
        playerTraform.eulerAngles = playerRot;



        //主要玩家的移动控制

        //定义三个变量
        float x = 0f, y = 0f, z = 0f;

        //前后左右的移动
        //前
        if(Input.GetKey(KeyCode.W))
        {
            z += playerSpeed * Time.deltaTime;
        }
        //后
        if(Input.GetKey(KeyCode.S))
        {
            z -= playerSpeed * Time.deltaTime;
        }

        //左
        if(Input.GetKey(KeyCode.A))
        {
            x -= playerSpeed * Time.deltaTime;
        }
        //右
        if(Input.GetKey(KeyCode.D))
        {
            x += playerSpeed * Time.deltaTime;
        }

        //当玩家跳起来的时候,角色控制器会自动检测玩家的跳起
        y -= playerGravity * Time.deltaTime;


        //移动
        playerCC.Move(playerTraform.TransformDirection(new Vector3(x,y,z)));

        //摄像机的位置和主角位置一致
        Vector3 pos = playerTraform.position;
        pos.y += camHeight;
        camTransform.position = pos;

    }


    /// <summary>
    ///用来显示游戏中主角的位置
    /// </summary>
    void OnDrawGizmos()
    {
        Gizmos.DrawIcon(this.transform.position,"Spawn.tif");
    }


    //射击
    public void OnDamage(int damage)
    {

        playerLife -= damage;

        //更新UI
        GameManager.Instance.SetLife((int)playerLife);

        //判断生命值是否为0
        if (playerLife <= 0)
        {
            Cursor.lockState = CursorLockMode.Confined;
        }
    }

}

  在斯类似中,比较关键之东西便是,在第一人称射击游戏受怎么通过鼠标的活动,转换成玩家视角的旋转,这个是其一仿佛中比较主要的地方,下面我将这等同部分的代码单独将出去:

void Control()
    {
        //获取鼠标的移动来进行方向的切换
        float h = Input.GetAxis("Mouse X");
        float v = Input.GetAxis("Mouse Y");

        //旋转摄像机设置
        camRot.x -= v;
        camRot.y += h;
        camTransform.eulerAngles = camRot;

        //设置主角的朝向与摄像机一致
        Vector3 playerRot = camTransform.eulerAngles;
        playerRot.x = 0;
        playerRot.z = 0;
        playerTraform.eulerAngles = playerRot;



        //主要玩家的移动控制

        //定义三个变量
        float x = 0f, y = 0f, z = 0f;

        //前后左右的移动
        //前
        if(Input.GetKey(KeyCode.W))
        {
            z += playerSpeed * Time.deltaTime;
        }
        //后
        if(Input.GetKey(KeyCode.S))
        {
            z -= playerSpeed * Time.deltaTime;
        }

        //左
        if(Input.GetKey(KeyCode.A))
        {
            x -= playerSpeed * Time.deltaTime;
        }
        //右
        if(Input.GetKey(KeyCode.D))
        {
            x += playerSpeed * Time.deltaTime;
        }

        //当玩家跳起来的时候,角色控制器会自动检测玩家的跳起
        y -= playerGravity * Time.deltaTime;


        //移动
        playerCC.Move(playerTraform.TransformDirection(new Vector3(x,y,z)));

        //摄像机的位置和主角位置一致
        Vector3 pos = playerTraform.position;
        pos.y += camHeight;
        camTransform.position = pos;

    }

下带大家整理一下,看看开发者在此老古董上都来啊创新之玩法:

    (二)控制脚本,

  这个剧本主要处理屏幕的UI方面,和游乐的片段逻辑处理

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance = null;

    //游戏得分
    int score = 0;
   //游戏最高分
    static int highScore = 0;

    //弹药数量
    int amout = 100;
    //游戏主角
    Player player;

    Text txtAmout;
    Text txtHighScore;
    Text txtLife;
    Text txtScore;

    Transform canvasTranform;

    void Start()
    {
        Instance = this;

        player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();

        canvasTranform = GameObject.FindGameObjectWithTag("UI").GetComponent<Transform>();


        //依次获取canvas 下面的UGUI的内容
        foreach(Transform t in canvasTranform.GetComponentsInChildren<Transform>())
        {
            if(t.name.CompareTo("boot")==0)
            {
                txtAmout= t.transform.GetComponentInChildren<Text>();
                Debug.Log(txtAmout.text);
            }

            if(t.name.CompareTo("life")==0)
            {
                txtLife = t.transform.GetComponentInChildren<Text>();

            }

            if(t.name.CompareTo("score")==0)
            {
                txtScore = t.GetComponent<Text>();
            }

            if(t.name.CompareTo("highscore")==0)
            {
                txtHighScore = t.GetComponent<Text>();
            }
        }

        //初始化分数为0
        SetScore(0);

    }

    //更新分数
    public void SetScore( int _score)
    {
        score += _score;
        //
        if(score>highScore)
        {
            highScore = score;
        }
        txtScore.text="Score <color=yellow>"+score+"</color>";
        txtHighScore.text = "HighScore " + highScore;
        txtHighScore.color = Color.red;
    }

    //更新弹药
    public void SetAmout(int _amout)
    {
        amout -= _amout;

        //如果弹药为负数,填充
        if(amout<=0)
        {
            amout = 100;
        }

        txtAmout.text = amout.ToString() + "/100";
    }

    //更新生命值
    public void SetLife(int _life)
    {
        txtLife.color = Color.red;
        txtLife.text = _life.ToString();
    }

    //
    void OnGUI()
    {
        if(player.playerLife<=0)
        {
            //居中显示
            GUI.skin.label.alignment = TextAnchor.MiddleCenter;

            //改变字体大小
            GUI.skin.label.fontSize = 40;

            //显示GameOver
            GUI.Label(new Rect(0, 0, Screen.width, Screen.height), "Game Over");

            //设置按钮文字大小
            GUI.skin.label.fontSize = 30;
            //显示重新开始
            if(GUI.Button(new Rect(Screen.width*0.5f-150,Screen.height*0.75f,300,40),"再来一次"))
            {
                //重新读入
                Application.LoadLevel(0);
            }
        }
    }
}
  • Snake
    Rewind
  • 【Android & iOS &
    WP;单机】当年盛行世界之诺基亚贪食蛇的原作者所发,在外的工作室中同时又做的新版贪食蛇。并多了各种非常道具,丰富玩法。

 (三)敌人的逻辑

  主要完成敌人的有些寻路处理,这里一个最主要之知识点就是寻路的安,这当打闹中凡是很重要之,需要编制敌人的Ai,这个类似中实现了敌人每间隔一截的光阴更新玩家的位置信息作敌人的目标,然后经过动画的控制来达成走,攻击等片段排的动作,主要是经过判断玩家和敌人之间的距离,其中动画部分的宏图为是以此备受之重要

using UnityEngine;
using System.Collections;

public class Enemy : MonoBehaviour
{

    //生成敌人的脚本
    protected EnemySpawn enemyspawn;

    Transform enemyTransform;

    //主角
    Player player;
     //寻路组件

    NavMeshAgent enemyAgent;

    //移动速度
    float enemySpeed=1.0f;

    //敌人旋转速度
    float enemyRotate = 30f;
    //计时器
    float enemyTime = 2f;

    float enemyLife = 5f;

    //获取动画组件
    Animator enemyAni;

    //

    void Start()
    {
        enemyTransform = this.transform;

        //获取主角
        player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();

        //获取寻路组件
        enemyAgent = this.gameObject.GetComponent<NavMeshAgent>();
        //改变敌人的移动速度

        enemyAni = this.gameObject.GetComponent<Animator>();

        enemyAgent.speed = enemySpeed;

        //设置寻路的目标 即敌人的目标玩家
        enemyAgent.SetDestination(player.playerTraform.position);

    }


    //用于旋转敌人,使敌人面向玩家
    void RotaeToPlayer()
    {
        //获取目标方向
        Vector3 targetDir = player.playerTraform.position - enemyTransform.position;

        //计算出新的方向
        Vector3 newDir = Vector3.RotateTowards(enemyTransform.forward, targetDir, enemyRotate * Time.deltaTime, 0);
        enemyTransform.rotation = Quaternion.LookRotation(newDir);
    }


    void Update()
    {
        //生命值为0
        if(enemyLife<=0)
        {
            return;
        }

        //获取当前动画状态
        AnimatorStateInfo stateInfo = enemyAni.GetCurrentAnimatorStateInfo(0);
        //如果处于待机状态//并且不是处于转换的时候

        if(stateInfo.fullPathHash==Animator.StringToHash("First.idle")&&!enemyAni.IsInTransition(0))
        {
            enemyAni.SetBool("idle",false);

            //待机一定时间
            enemyTime -= Time.deltaTime;
            if(enemyTime<=0)
            {
                return;
            }

            //如果小于某个距离进行攻击
            if(Vector3.Distance(enemyTransform.position,player.playerTraform.position)<1.5f)
            {
                enemyAni.SetBool("attack", true);
            }

            else
            {

                //重置定时器
                enemyTime = 1f;

                //设置目标
                enemyAgent.SetDestination(player.playerTraform.position);

                //进入跑步的动画
                enemyAni.SetBool("run",true);

            }
        }

        //处于跑步的状态
        if(stateInfo.fullPathHash==Animator.StringToHash("First.run")&&!enemyAni.IsInTransition(0))
        {
            enemyAni.SetBool("run",false);

            //每隔一秒定位一次
            enemyTime -= Time.deltaTime;

            if(enemyTime<=0)
            {
                //重新开始寻路
                enemyAgent.Resume();

                enemyAgent.SetDestination(player.playerTraform.position);
                enemyTime = 1;

            }
            //距离小于1.5
            if(Vector3.Distance(enemyTransform.position,player.playerTraform.position)<1.5)
            {
                //停止寻路
                enemyAgent.Stop();

                enemyAni.SetBool("attack",true);
            }
        }

        //当处于攻击状态
        if(stateInfo.fullPathHash==Animator.StringToHash("First.attack")&&!enemyAni.IsInTransition(0))
        {
            //面向主角
            enemyAni.SetBool("attack",false);

            //如果动画播放完,重新待机
            if(stateInfo.normalizedTime>=1f)
            {
                enemyAni.SetBool("idle", true);

                //重置
                enemyTime = 2;

                //更新主角的生命值
                player.OnDamage(1);
            }
        }




        ////
        ////
        ////
        ////这里有一个Bug,不能进入death中,,,,,,,
        ////
        ///
        ////
        ///
        //当播放死亡的动画的时候
        if(stateInfo.fullPathHash==Animator.StringToHash("First.death"))
        {

            Debug.Log("进入");
            enemyTime-=Time.deltaTime;
            //播放完死亡的动画
            if(enemyTime<=0)
            {

                //减少敌人的数量
                enemyspawn.enemyCount--;

                //加分
                GameManager.Instance.SetScore(100);
                //摧毁自身

                Destroy(this.gameObject);
                Debug.Log("播放了死亡");


            }
        }

    }


    //计算伤害的函数
    public void OnDamage(int damage)
    {
        enemyLife -= damage;

        if(enemyLife<=0)
        {
            float time2 = Time.time;

            //停止寻路
            enemyAgent.Stop();
            //播放死亡动画
            enemyAni.SetBool("death",true);
        }
    }


    public void Init(EnemySpawn scirpt)
    {
        enemyspawn = scirpt;

        //增加敌人数量
        enemyspawn.enemyCount++;
    }
}

 

  • Curvytron
  • 【Web,网游】多人口贪食蛇游戏。你可和海内外的玩家当网页上合打这个娱乐。如果您挂了,你的遗骸就见面成为他人的阻力。坚持到最终之人才会获制胜。

 (四)一些用到的切近

    1.机动销毁类

  

using UnityEngine;
using System.Collections;

public class AutoDestroy : MonoBehaviour
{
    public float timer = 1.0f;

    void Start()
    {
        Destroy(this.gameObject, timer);
    }
}

  2.敌人生成

using UnityEngine;
using System.Collections;

public class EnemySpawn : MonoBehaviour
{
    //定义变量
    //敌人的prefab
    public GameObject enemyObj;
    //生成敌人的数量
    public int enemyCount;

    //敌人生成的最大数量
    public float enemyMaxCount;

    //敌人生成的时间间隔
    public float enemyTimer;

    Transform thisTransform;


    void Start()
    {
        thisTransform = this.transform;
    }


    void Update()
    {
        //生成敌人的数量达到最大值时候,停止生成敌人
        if(enemyCount>=enemyMaxCount)
        {
            return;
        }

        //每隔一段时间生成

        enemyTimer -= Time.deltaTime;

        if(enemyTimer<=0)
        {
            //得到下一轮敌人的生成时间
            enemyTimer = Random.Range(5, 15);

            GameObject tar = (GameObject)Instantiate(enemyObj, thisTransform.position, Quaternion.identity);

            Enemy enemyScript = tar.GetComponent<Enemy>();

            enemyScript.Init(this);
        }
    }
    void OnDrawGizmos()
    {
        Gizmos.DrawIcon(transform.position,"item.png");
    }
}

  3.多少窗口的创办与展示,这里最主要透过不同摄像机显示不同的layer来实现

using UnityEngine;
using System.Collections;



[AddComponentMenu("Game/MiniCamera")]
public class MiniCamera : MonoBehaviour
{

    // Use this for initialization
    void Start()
    {
        float ratio = (float)Screen.width / Screen.height;

        this.GetComponent<Camera>().rect = new Rect((1-0.2f),(1-0.2f*ratio),0.2f,0.2f*ratio);
    }

}

 

  • Curvatron
  • 【Web &
    Android;单机多丁】和好友于一个屏幕及游玩贪食蛇。和过去的贪食蛇直行不同,这长达蛇只见面转圆圈。手机版支持双人游戏,网页版支持
    8 称玩家共享喜悦。
  • Snake
    ’97
  • 【iOS & Android & WP;游戏】97 复古版的贪食蛇游戏,专供回忆。
snake.PNG
  • Silly Sausage in Meat
    Land
  • 【iOS;单机】腊肠狗也易贪食蛇,一种横版玩法。有添加道具。
  • DoodleGrub
  • 【Android & iOS &
    WP等;单机】涂鸦风格的贪食蛇。道具种类是贪食蛇类最丰富的。
  • Snaky
    Lines
  • 【Android;单机】炫酷霓虹效果的竞速贪食蛇。(其实是稍稍蝌蚪)
  • Quetzalcoatl
  • 【iOS;单机】最容易折腾虐心小游戏的1Button
    SARL做的同等悠悠类似贪食蛇的玩。本周成了AppStore一完善最佳榜产品。虽然还是是蛇形主角在屏幕上走,但玩法及再次如是华容道。
  • Agar.io
  • 【Web;网游】这卖准确之说应该给贪食球。因为吃食后未见面变长而是变充分。据称在B站充分火,有人叫和Curvytron齐名的“大”作。这吗是一个在线网游,有事可能上万总人口联手比。最为根本的凡,这个游乐被,玩家可可以互相吞食的。大鱼吃小鱼是这个玩的最为重点规则。

假若你发觉又多好玩之玩法,或者从中得到什么启示。也意在能够共同讨论。

发表评论

电子邮件地址不会被公开。 必填项已用*标注