基于unity制作的游戏《ZERO:天元》

Posted 仿生程序员会梦见电子羊吗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于unity制作的游戏《ZERO:天元》相关的知识,希望对你有一定的参考价值。

简介

《计算与人工智能概论》期末大作业项目
项目名《ZERO:天元》
类型:游戏

米呼游詹姆斯·克拉克·麦克斯韦方程组工作室出品

故事背景:
弗兰大学研究生唐可可一日从寝室醒来,发现睡过头了,但全楼一片死寂。这时,一个电话打来……

唐可可是弗兰大学一名研究生,专攻生物信息学,目前在导师的实验室负责人工智能与模式识别,当然,因为导师的课题属于涉密项目,参与者很少,所以从图形图像处理到自然语言处理都是他一个人在干,
他常常戏称自己是弗兰大学病毒学课题组里计算机学得最好的。
导师的项目与学校的超算中心有合作,这样他也可以进入层层设防的中心内部,一睹当时最先进的超级计算机“天河”的风采,
当然,还有机会接近基于强大算力的顶尖人工智能——“天元(ZERO)”。

弗兰大学:星沙市综合性大学,从考古学到生物学无所不包,还有全国最强超算中心,和那台著名的“天河”。
当然,更让人感兴趣的是名为“天元”的人工智能,有风声道,“天元”在图灵测试中表现“相当优异”。
在击败著名人工智能AlphaGO之后,“天元”宣判了深度学习的死刑,将众人的目光由卷积神经网络炼丹转向生命科学与人工智能交叉领域。
21世纪,生命科学的时代到来了。

考古系将最新发现的奥陶纪化石转交给了超算中心,希望基因测序可以解析其中发现的病毒“奥陶琵斯”。
当然,这是对外发布的说法。
也有人说,在超算中心的地下有着迷宫般复杂而庞大的生物实验室,
而“始祖病毒”奥陶琵斯,正是这座地下基地最深层的秘密。
后来,这个人因散播谣言被拘留十日处理。

“‘始祖’是一种非常罕见的病毒,令人难以置信,仿佛天工造物,精妙绝伦,在细胞分裂的逆向工程中有着超越已知科学的效果,宛若神迹。或许这是上帝给人类的恩赐。”
——美国著名生物学家威斯克在《Nature》上如此称赞道。
据说,每一项对于“奥陶琵斯”的研究都能轻易在《N》&《S》上发表,
所以一时国内外生化环材学者均涌向弗兰大学,希望得到化石样本。
不过很快,对于化石的研究被列入绝密项目,而样本也被移送到同样颇负盛名的超算中心。

游戏玩法:操纵人物在地图移动,探索看似平静的校园之下隐藏的秘密。



功能实现:unity游戏引擎,python,AE,PR,Ubuntu
核心玩法:文字解密,走剧情探索
辅助玩法:pygame作为主角在地下世界闯关时的解密,以游戏的形式代替枯燥的密码解谜。
项目地址:
Algernon98/pygame-in-HNU: python project in HuNan University (github.com)
图像处理:图片素材使用python代码对其像素化,作为地图素材,已被封装好可以直接调用程序。
人脸识别:openCV作为人脸识别资源库,代码在linux环境下可以调用摄像头对人脸识别,可以嵌入游戏中,作为“天元”boss房的 进入验证,可以增加沉浸感。

解密功能:采用MINST数据集,依照故事背景,黑客出身的主角可以破解地下基地的系统门禁。
我们在IRIS鸢尾花数据集、波士顿房价数据集等知名机器学习数据集中选择手写数字识别,
以其深度学习“Hello World”的称号,呼应“天元”这一哲学与棋道双重含义。

详情可见博客:
【Python机器学习基础教程】(三)_仿生程序员会梦见电子羊吗的博客-CSDN博客

代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class playercontroller : MonoBehaviour


    public Rigidbody2D rb;
    public float speed ;
    public float jumpforce;
    public Animator anim;
    public LayerMask ground;
    public Collider2D coll;

    // Start is called before the first frame update
    void Start()
    
        
    

    // Update is called once per frame
    void Update()
    
        Movement();
        SwitchAnim();
    

    void Movement()
    
        float horizontalmove;
        horizontalmove=Input.GetAxis("Horizontal");
        float facedirction=Input.GetAxisRaw("Horizontal");
        if (horizontalmove !=0)
        
            rb.velocity = new Vector2(horizontalmove*speed,rb.velocity.y);
            anim.SetFloat("running",Mathf.Abs(facedirction));
        
        if (facedirction !=0)
        
            transform.localScale=new Vector3(facedirction,1,1);
        

        if(Input.GetButtonDown("Jump"))
        
            rb.velocity=new Vector2(rb.velocity.x,jumpforce);
            anim.SetBool("jumping",true);
        

    

    void SwitchAnim()
    
        anim.SetBool("idle",false);
        if(anim.GetBool("jumping"))
        
            if(rb.velocity.y<0)
            
                anim.SetBool("jumping",false);
                anim.SetBool("falling",true);
            
        else if(coll.IsTouchingLayers(ground))
        
            anim.SetBool("falling",false);
            anim.SetBool("idle",true);
        
    

剧情PV

https://www.bilibili.com/video/BV1jB4y1X7za/?spm_id_from=333.999.0.0&vd_source=11bfc591eb1189ab7412b09ee29e1dcd
湖南大学大作业《ZERO:天元》剧情PV

实机演示

https://www.bilibili.com/video/BV1uv4y1w7Vr/?spm_id_from=333.999.0.0&vd_source=11bfc591eb1189ab7412b09ee29e1dcd
湖南大学期末大作业《ZERO:天元》demo实机演示

我用Unity制作的第一款游戏Demo

我用Unity制作的第一款游戏Demo

Immortal(不朽)


游戏网址:https://play.unity.com/mg/other/immortal_webgl
或者:点击此处进行游戏

或者:下载游戏 提取码:qv8p

这是我使用 Unity 制作的第一款游戏Demo,一款2D的RPG游戏。
游戏中所有工作均为本人独立完成。

美术方面

哎,早知道该好好学学画画的,对队来说制作这个Demo最耗时、最难的工作就是画画了,画个老半天还是惨不忍睹。

为了不至于不堪入目,果断选择了2D像素风格,游戏中所有的Sprite和Image都是使用Aseprite制作的,比起PS,Aseprite绘制像素画太方便了,爱了爱了。
不知不觉都肝了150多个小时了。。。
角色移动动画

爆炸动画

每个像素都是泪呀T T

技术方面

比较难的点就是对象池的制作,为了保证可扩展性,对象池是用字典存储的,字典通过结构体数组的方式序列化(能在Inspector里直接添加对象简直舒服)。下面是对象池实现的C#代码:

/// <summary>
/// 对象池
/// </summary>
public class ObjectPool : MonoBehaviour

    //单例模式 
    public static ObjectPool instance;
    //对象类别
    public enum ObjectType
    
        Shadow,
        FireBall,
        FireExplosion,
        Wind
    

    #region 字典的序列化
    [System.Serializable]
    //可序列化的结构体数组(为了序列化字典)
    public struct ObjectDetail
    
        public ObjectType type; //类别
        public GameObject prefab; //预制体
        public int count; //数量
    
    [Header("对象池中的内容")]
    public ObjectDetail[] objectDetails;
    #endregion

    //缓存字典<对象类别, 对象队列>
    public Dictionary<ObjectType, Queue<GameObject>> cache = new Dictionary<ObjectType, Queue<GameObject>>();

    private void Awake()
    
        //单例模式
        instance = this;
        //初始化对象池
        Initialization();
    

    /// <summary>
    /// 初始化对象池并填满
    /// </summary>
    private void Initialization()
    
        foreach (var item in objectDetails)
        
            cache.Add(item.type, new Queue<GameObject>());
            for (int i = 0; i < item.count; i++)
            
                var newObject = Instantiate(item.prefab);
                newObject.transform.SetParent(transform);
                newObject.SetActive(false);
                cache[item.type].Enqueue(newObject);
            
        
    
    /// <summary>
    /// 填满对象池
    /// </summary>
    /// <param name="objectType">对象类别</param>
    public void FillPoll(ObjectType type)
    
        foreach (var item in objectDetails)
        
            if (item.type == type)
            
                for (int i = 0; i < item.count; i++)
                
                    var newObject = Instantiate(item.prefab);
                    newObject.transform.SetParent(transform);
                    newObject.SetActive(false);
                    cache[type].Enqueue(newObject);
                
                return;
            
        
    
    /// <summary>
    /// 返还给对象池
    /// </summary>
    /// <param name="type">对象类别</param>
    /// <param name="gameObject">对象</param>
    public void ReturnPool(ObjectType type, GameObject gameObject)
    
        gameObject.SetActive(false);
        cache[type].Enqueue(gameObject);
    
    /// <summary>
    /// 取出一个对象
    /// </summary>
    /// <param name="type">对象类别</param>
    /// <returns>取出的对象</returns>
    public GameObject GetFromPool(ObjectType type)
    
        if (cache[type].Count == 0)
        
            FillPoll(type);
        
        //出队
        var outObject = cache[type].Dequeue();
        //启用
        outObject.SetActive(true);

        return outObject;
    

技能系统的设计也比较难,为了保证可扩展性,代码改了又改,下面是技能类的实现和火球术的实现。
技能类的实现:

/// <summary>
/// 技能类
/// </summary>
public class Skill

    protected PlayerSkillController playerSkillController; //玩家技能控制器
    public Image CDImage; //CD的UI组件
    //冷却时间
    protected float coolDown;
    //冷却时间计时器
    protected float cdTimer;
    //技能在技能栏中的位置
    private int index;
    //是否可用
    public bool Available  get; set; 

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="coolDown">冷却时间</param>
    /// <param name="index">技能在技能栏中的位置</param>
    protected Skill(float coolDown, int index, PlayerSkillController playerSkillController)
    
        this.coolDown = coolDown;
        cdTimer = coolDown;
        Available = true;
        this.index = index;
        this.playerSkillController = playerSkillController;
    

    /// <summary>
    /// 计算冷却时间
    /// </summary>
    protected void UpdateCD()
    
        if (cdTimer < coolDown)
        
            cdTimer += Time.deltaTime;
        
        else
        
            cdTimer = coolDown;
        
        CDImage.fillAmount = (coolDown - cdTimer) / coolDown;
    

    /// <summary>
    /// 使用技能
    /// </summary>
    /// <returns>使用成功返回true; 失败返回false</returns>
    protected bool Use()
    
        if (cdTimer == coolDown && Available)
        
            cdTimer = 0;
            return true;
        
        return false;
    

火球术的实现:

/// <summary>
/// 火球术
/// </summary>
public class FireBall : Skill, ISkill

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="coolDown">冷却时间</param>
    /// <param name="index">技能在技能栏中的位置</param>
    /// <param name="playerSkillController">玩家技能控制器</param>
    public FireBall(float coolDown, int index, PlayerSkillController playerSkillController) : base(coolDown, index, playerSkillController)
    
        CDImage = GameObject.FindWithTag("FireBallCD").GetComponent<Image>();
    

    public void Update()
    
        UpdateCD();
    

    public new bool Use()
    
        if (base.Use())
        
            cdTimer = 0;
            GameObject fireBall = ObjectPool.instance.GetFromPool(ObjectPool.ObjectType.FireBall);
            fireBall.GetComponent<FireBallController>().SetProperty(
                playerSkillController.fireBallActiveTime,
                playerSkillController.fireBallDamage,
                playerSkillController.fireBallSpeed,
                playerSkillController.skillDirection,
                playerSkillController.transform.position
                );
            return true;
        
        return false;
    

游戏内容


游戏目前实现了人物的移动和三种不同类型的技能,怪物的移动和两种不同类型的技能,人物与怪物的受伤提示(颜色变化)和死亡效果,告示牌的互动对话UI系统,游戏的菜单UI系统,游戏的暂停、重开和退出功能,小地图功能等。

技能介绍:
火球术:通过对象池实现。使用了拖尾效果和粒子系统,包括飞行时间限制、碰撞检测、击中碰撞器发生爆炸的效果。

疾步:通过对象池实现人物残影效果,移速增加Buff。

掌心雷:通过Trigger实现检测怪物与三段伤害,通过RaycastHit2D实现遮挡识别。

怪物技能:
旋风:通过对象池实现,通过旋转矩阵实现8个方向同时发射。

风阵:通过Trigger实现检测玩家与持续伤害。

怪物实现:
通过仇恨距离与RaycastHit2D实现怪物仇恨系统与追随玩家功能。

通过随机方式选择怪物技能。

部分C#脚本:

推荐教程:
B站UP主: M_Studio
YouTuber:CouchFerret makes GamesB站搬运

以上是关于基于unity制作的游戏《ZERO:天元》的主要内容,如果未能解决你的问题,请参考以下文章

unity制作赛车游戏,车子加了刚体和碰撞体为啥车子死活不动?

Unity教程1:如何切割我的图片素材并使用tilemap搭建游戏背景(Pixels Per Unit设置不合适问题)

2018 Unite大会——《使用UPA工具优化项目》演讲实录

Unite'17 Shanghai再一次问候

Unite'17 Shanghai再一次问候

关于《Unity3D/2D游戏开发从0到1》书籍再版说明