一:逻辑梳理

要实现的功能

游戏操作

在规定时间内,垃圾通过拖拽进入正确垃圾桶的容器,垃圾在这里消失,飞入第二个屏上对应垃圾桶的位置并实现加分和加时间的效果,垃圾拖拽进入不正确的垃圾桶,垃圾会返回到原来的位置,同时,相应的时间也会减少

胜利和失败的条件:

胜利:垃圾全部分类完毕,并且时间不为零,游戏胜利,

失败:时间为0,垃圾未分类结束,游戏失败

游戏结束后,可以点击屏幕可以重新进入游戏,重新进入游戏时,垃圾的排列会发生变化

游戏代码梳理

需要一个每个垃圾自己的类,涉及垃圾自己的属性,都放在这个类里面,例如:垃圾的拖拽,整个游戏中,只有垃圾需要拖拽的功能,这个是只属于垃圾的功能,就放在这个类里面

每个垃圾桶自己的类,涉及垃圾桶的属性,都放在这里面,例如:垃圾进入和离开垃圾桶的碰撞器

垃圾和垃圾桶之间交互的管理类,例如:垃圾和垃圾桶之间分类正确加分,错误减分,垃圾的布局

游戏胜利和失败等都是在这个类中

双屏显示的类

二:效果展示

三:UI布局和代码书写

1.双屏的设置

新建两个canvas,分别命名:MainCanvas和ViceCanvas,新建一个摄像机,名为:ViceCamera

设置MainCamera和MainCanvas属性,如图所示

ViceCamera和ViceCanvas的Target Display设置为DisPlay2,把ViceCamera上面的Audio Listence删掉,一个场景中只需要一个Audio Listence

新建一个C#脚本,名为:SplitScreen,编写代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 分屏代码
/// </summary>
public class SplitScreen : MonoBehaviour
{void Start(){if (Display.displays.Length > 1)Display.displays[1].Activate();if (Display.displays.Length > 2)Display.displays[2].Activate();}
}

二:垃圾的设置

在MainCanvas下面新建一个空物体:GarbagePanel ,铺满全屏,管理类的代码放在这个上面,一直都显示

在GarbagePanel下面新建一个空物体:root,铺满全屏,作为垃圾分类主界面的容器,所有的图片文字视频都放在这里面

在root下

新建图片:bg,铺满全屏,把背景图片拖进去,

新建空物体:garbageItemContent,铺满全屏,用来存放所有垃圾,把所有的垃圾都拖进来,作为他的子物体,每个垃圾都添加BoxCollide2Dr和Rigibody2D,手动调节碰撞盒的大小,(这里设置的是100*100),将Rigibody2D的重力值大小设为0(防止垃圾往下坠落)设置垃圾的标签,不同的垃圾设置不同的标签recycle,dry,wet,hamful

新建C#脚本:GarbageItem,运用鼠标拖拽的三个接口实现垃圾拖拽的操作

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;public class GarbageItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{Vector2 offest;         //鼠标的偏移量Vector2 startPos;       //垃圾初始位置Vector2 oldPos;         //鼠标拖拽结束之后的位置RectTransform rect;     //垃圾的RectTransform(当垃圾消失(垃圾的setactive为false)时,垃圾的RectTransform为空)//组件初始化void InitCompoment(){if (rect==null){rect = GetComponent<RectTransform>();}}//数据初始化void InitData(Vector2 pos){//组件初始化InitCompoment();//位置初始化startPos = pos;RestoreOriginPos();//TODO:与垃圾桶的关系}//位置修改void RestoreOriginPos(){//获取初始位置startPos = rect.anchoredPosition;}//鼠标开始拖拽public void OnBeginDrag(PointerEventData eventData){//拖拽过程中的偏移量offest = eventData.position - (Vector2)transform.position;}//鼠标拖拽过程public void OnDrag(PointerEventData eventData){//垃圾的位置限制,不能拖出屏幕外Vector2 pos = eventData.position - offest;transform.position = new Vector3(Mathf.Clamp(pos.x, 0, Screen.width), Mathf.Clamp(pos.y, 0, Screen.width), 0);//拖拽完毕后的位置oldPos = eventData.position - offest;}//拖拽结束public void OnEndDrag(PointerEventData eventData){//TODO:完成加分,加时间等操作,需要与垃圾桶,和管理者交互}
}

但是,我们只是做到了垃圾的拖拽,但是与垃圾桶的交互和游戏的逻辑制作,后面会做这些工作

三:垃圾桶的设置

新建一个空物体:TrashCanRoot,放在屏幕上方,调整大小

在TrashCanRoot的下面新建四个图片,代表四种类型的垃圾桶,每个垃圾筒上都添加BoxCollider2D和Rigidbody2D组件,在BoxCollider2D中勾选IsTrigger,设置Rigidbody2D的重力值为0,并为每个垃圾桶添加标签,如图所示

新建C#脚本:Trash

在GarbageItem中添加垃圾桶的引用,并实现封装

在数据初始化中添加:Trash = null;

在鼠标拖拽结束的代码中,添加

编辑Trash脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 垃圾桶
/// </summary>
public class Trash : MonoBehaviour
{#region   垃圾进入垃圾桶的碰撞器private void OnTriggerEnter2D(Collider2D collision){if (collision.transform.GetComponent<GarbageItem>() != null)collision.transform.GetComponent<GarbageItem>().Trash = this;}#endregion#region 物体离开垃圾桶的碰撞器private void OnTriggerExit2D(Collider2D collision){//离开后设置垃圾桶为空if (collision.transform.GetComponent<GarbageItem>() != null)collision.transform.GetComponent<GarbageItem>().Trash = null;}#endregion}

副屏上面的界面

四:游戏管理者

新建四个空物体,挂载Audio Source组件,把对应的音效文件拖入Clip中,如图

编写脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.Audio;
using Interface;
using DG.Tweening;/// <summary>
/// 垃圾分类面板脚本
/// </summary>
public class GarbagePanel : MonoBehaviour, BubbleContent
{public bool isGameOver;  //游戏是否结束 true结束 false未结束int createNumber = 0;    //随机次数 为了创建垃圾item的不同位置int sumScore = 0;        //总分数(由垃圾item容器子物体数量决定)int timer = 300;         //时间public int score = 0;           //分数GarbageItem currentItem; //当前正在拖拽的垃圾itemTweener addTimerTweener; //加减时间动画[Header("游戏最大持续时间")]public int maxTimer = 180;[Header("退出按钮")][SerializeField] Button quitBtn;[Header("正确、错误、游戏胜利、游戏失败音效")][SerializeField] AudioSource correntAudio;[SerializeField] AudioSource errorAudio;[SerializeField] AudioSource victoryAudio;[SerializeField] AudioSource defeatedAudio;[Header("游戏胜利/失败Text,2屏时间、分数、加时间Text,点击屏幕刷新游戏按钮")][SerializeField] Text gameOverTxt;public Text timeTxt;public Text scoreTxt;[SerializeField] Button refreshBtn;[Header("2屏加时间Text,加减分颜色")][SerializeField] Color addColor;[SerializeField] Color subtractColor;[SerializeField] Text addTimerTxt;[Header("加分星星动画预设,星星容器")][SerializeField] Transform starPrefab;[SerializeField] Transform starContent;List<Transform> starPool; //星星对象池List<Transform> moveStarList; //正在播放的星星特效[Header("2屏的垃圾桶位置")]public Transform twoSceneTrashPos;[Header("1屏 所有垃圾item存放容器")]public Transform garbageItemContent;[Header("垃圾分类root,2屏垃圾分类界面")][SerializeField] GameObject root;[SerializeField] GameObject twoSceneRoot;List<GarbageItem> moveGarbageList;                  //正在dotween移动的垃圾itemList<Vector2> garbageItemPosRotList;                //垃圾item的所有位置Dictionary<string, Transform> twoSceneTrashPosDic;  //2屏的垃圾桶位置字典public Dictionary<string, Transform> TwoSceneTrashPosDic { get => twoSceneTrashPosDic; set => twoSceneTrashPosDic = value; } //封装 GarbageItem调用public List<GarbageItem> MoveGarbageList { get => moveGarbageList; }public GarbageItem CurrentItem { get => currentItem; set => currentItem = value; }//单例static GarbagePanel instance;public static GarbagePanel GetInstance(){return instance;}void Awake(){instance = this;}void Start(){starPool = new List<Transform>();moveStarList = new List<Transform>();moveGarbageList = new List<GarbageItem>();//DoTween动画addTimerTweener = addTimerTxt.DOFade(0, 2f);addTimerTweener.SetAutoKill(false);addTimerTweener.Pause();//默认颜色透明addTimerTxt.color = new Color32(0, 0, 0, 0);//初始化所有垃圾的位置garbageItemPosRotList = new List<Vector2>();foreach (Transform item in garbageItemContent){garbageItemPosRotList.Add(item.GetComponent<RectTransform>().anchoredPosition);}//2垃圾桶位置字典初始化twoSceneTrashPosDic = new Dictionary<string, Transform>();foreach (Transform item in twoSceneTrashPos){twoSceneTrashPosDic.Add(item.tag, item);}//按钮监听quitBtn.onClick.AddListener(ReturnMainPanel);refreshBtn.onClick.AddListener(RefreshGame);}#region 游戏刷新IEnumerator timerIE; //时间倒数void RefreshGame(){//游戏未结束isGameOver = false;//不为空 先停止协程,然后在开启,防止冲突if (timerIE != null)StopCoroutine(timerIE);//音效停止StopAllAudio();//隐藏刷新,游戏结束提示按钮refreshBtn.gameObject.SetActive(false);gameOverTxt.gameObject.SetActive(false);//回收所有加分星星RecycleAllStar();//刷新垃圾item布局RefreshGarbage();//总分数、时间、分数 初始化sumScore = garbageItemContent.childCount;score = 0;timer = maxTimer;scoreTxt.text = "分数:" + score.ToString();timeTxt.text = "时间:" + timer.ToString();//开启协程timerIE = GameTime();StartCoroutine(timerIE);}#endregion#region 刷新垃圾item布局public void RefreshGarbage(){int count = garbageItemContent.childCount;//创建次数超过垃圾最大数量时为0if (createNumber > count - 1)createNumber = 0;for (int i = 0; i < count; i++){int index = i + createNumber;//当下标超出最后一位时为0if (index > count - 1)index = Mathf.Abs(count - index);//位置初始化garbageItemContent.GetChild(i).GetComponent<GarbageItem>().InitData(garbageItemPosRotList[index]);}//创建数量加1createNumber++;}#endregion#region 游戏时间IEnumerator GameTime(){while (timer > 0){yield return new WaitForSeconds(1f);//当游戏时间结束前,游戏胜利可以将timer等于-1timer--;if (timer<=0)timer = 0;timeTxt.text = "时间:" + timer;}//停止所有正在移动的垃圾item和星星动画StopAllGarbageItemAndStar();//游戏结束判断if (score >= sumScore){Debug.Log("时间结束:" + score + " child:" + sumScore);GameVictory();  //游戏胜利}elseGameDefeated(); //游戏失败}/// <summary>/// 游戏胜利/// </summary>void GameVictory(){//游戏结束isGameOver = true;//停止所有正在移动的垃圾item和星星动画StopAllGarbageItemAndStar();VictoryAudio(); //胜利音效//刷新按钮,游戏结束Txt显示refreshBtn.gameObject.SetActive(true);gameOverTxt.gameObject.SetActive(true);gameOverTxt.text = "游戏胜利";}/// <summary>/// 游戏失败/// </summary>void GameDefeated(){//游戏结束isGameOver = true;//停止所有正在移动的垃圾item和星星动画StopAllGarbageItemAndStar();DefeatedAudio();//失败音效//刷新按钮,游戏结束Txt显示refreshBtn.gameObject.SetActive(true);gameOverTxt.gameObject.SetActive(true);gameOverTxt.text = "游戏失败";}#endregion#region 加分(星星效果)/// <summary>/// 加分/// </summary>/// <param name="pos">垃圾item消失的位置</param>public void AddScore(Vector3 pos){//获取星星动画,修改位置Transform star = GetStar();star.position = pos;star.gameObject.SetActive(true);//星星正在播放 添加到列表moveStarList.Add(star);}//获取星星Transform GetStar(){Transform star;//判断对象池是否存在itemif (starPool.Count > 0){star = starPool[0];starPool.Remove(star);}elsestar = Instantiate(starPrefab, starContent);return star;}//星星回收(动画播放完毕后,回收星星到对象池中)由动画关键帧调用public void RecycleStar(Transform star){/*回收*///星星播放结束 从列表移除moveStarList.Remove(star);star.gameObject.SetActive(false);starPool.Add(star);加分//score++;//scoreTxt.text = "分数:" + score.ToString();加时间//ADDTimer();播放音效//CorrentAudio();//判断所有垃圾分类完成if (score >= sumScore){Debug.Log("加分结束胜利:" + score + " child:" + sumScore);//停止时间协程StopCoroutine(timerIE);GameVictory();  //游戏胜利}}/// <summary>/// 回收所有加分星星/// </summary>void RecycleAllStar(){for (int i = 0; i < starContent.childCount; i++){Transform star = starContent.GetChild(i);star.gameObject.SetActive(false);//对象池中不存在此星星时,回收if (starPool.IndexOf(star) == -1)starPool.Add(star);}}#endregion#region 加减游戏时间/// <summary>/// 游戏时间+2秒/// </summary>public void ADDTimer(){addTimerTxt.text = "时间+2";addTimerTxt.color = addColor;addTimerTweener.Restart();timer += 2;if (timer >= maxTimer)timer = maxTimer;timeTxt.text = "时间:" + timer.ToString();}/// <summary>/// 游戏时间-2秒/// </summary>public void SubtractTimer(){addTimerTxt.text = "时间-2";addTimerTxt.color = subtractColor;addTimerTweener.Restart();timer -= 2;if (timer <= 0){timer = 0;//游戏失败GameDefeated();}timeTxt.text = "时间:" + timer.ToString();}#endregion#region 隐藏并停止所有动画及效果/// <summary>/// 隐藏并停止所有垃圾动画效果、星星效果、正在拖拽的物体/// 防止游戏结束时,/// </summary>void StopAllGarbageItemAndStar(){if (currentItem != null){currentItem.RestoreOriginPos();currentItem = null;}//循环所有在移动的itemfor (int i = 0; i < MoveGarbageList.Count; i++)MoveGarbageList[i].StopDoTween(); //停止dotween动画for (int i = 0; i < moveStarList.Count; i++){//隐藏星星moveStarList[i].gameObject.SetActive(false);//添加到对象池中starPool.Add(moveStarList[i]);}}#endregion#region 音效播放/// <summary>/// 正确/// </summary>public void CorrentAudio(){correntAudio.Play();}/// <summary>/// 错误/// </summary>public void ErrorAudio(){errorAudio.Play();}/// <summary>/// 胜利/// </summary>public void VictoryAudio(){victoryAudio.Play();}/// <summary>/// 失败/// </summary>public void DefeatedAudio(){defeatedAudio.Play();}/// <summary>/// 停止所有音效/// </summary>public void StopAllAudio(){correntAudio.Stop();errorAudio.Stop();victoryAudio.Stop();defeatedAudio.Stop();}#endregion}

GarbageItem中代码的添加和修改

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using DG.Tweening;/// <summary>
/// 垃圾Item
/// </summary>
public class GarbageItem : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{Vector2 offset;           //偏移量Vector2 oldPos;           //鼠标拖拽结束之后的位置Vector2 originPos;        //物体原点位置RectTransform rect;Trash trash;              //垃圾桶public Trash Trash { get => trash; set => trash = value; }#region 组件初始化void ThisGetCompontent(){if (rect == null){rect = GetComponent<RectTransform>();}}#endregion#region 数据初始化public void InitData(Vector2 pos){//组件初始化ThisGetCompontent();//刷新后 垃圾桶为空Trash = null;//位置修改originPos = pos;RestoreOriginPos();//显示gameObject.SetActive(true);}#endregion//位置还原public void RestoreOriginPos() {rect.anchoredPosition = originPos;}//开始拖拽public void OnBeginDrag(PointerEventData eventData){GarbagePanel.GetInstance().CurrentItem = this;offset = eventData.position - (Vector2)transform.position;}//拖拽public void OnDrag(PointerEventData eventData){//游戏结束 不执行操作if (GarbagePanel.GetInstance().isGameOver)return;Vector3 pos = eventData.position - offset;transform.position = new Vector3(Mathf.Clamp(pos.x, 0, Screen.width), Mathf.Clamp(pos.y, 0, Screen.height), 0);oldPos = eventData.position - offset;}//拖拽结束Tweener tweener;public void OnEndDrag(PointerEventData eventData){//游戏结束 不执行操作if (GarbagePanel.GetInstance().isGameOver) return;//垃圾桶不为空,代表垃圾进入了垃圾桶if (trash != null){//判断垃圾与垃圾桶的tag是否相同if (trash.tag==transform.tag){//Debug.Log("正确,执行位移,并加分");//加分GarbagePanel.GetInstance().score++;GarbagePanel.GetInstance().scoreTxt.text = "分数:" + GarbagePanel.GetInstance().score.ToString();//加时间GarbagePanel.GetInstance().ADDTimer();// 播放音效GarbagePanel.GetInstance(). CorrentAudio();//dotween动画正在移动,添加自己GarbagePanel.GetInstance().MoveGarbageList.Add(this);//修改垃圾item在2屏的垃圾桶位置rect.anchoredPosition = originPos;Debug.Log(rect);rect.SetParent(GarbagePanel.GetInstance().twoSceneTrashPos);rect.anchoredPosition = GarbagePanel.GetInstance().TwoSceneTrashPosDic[transform.tag].GetComponent<RectTransform>().anchoredPosition;tweener = rect.DOAnchorPos(new Vector2(rect.anchoredPosition.x, -14), 1f);tweener.OnComplete((TweenCallback)delegate{//Debug.Log("消失");//dotween移动结束,移除自己GarbagePanel.GetInstance().MoveGarbageList.Remove(this);//加分GarbagePanel.GetInstance().AddScore(transform.position);//修改为1屏的父物体gameObject.SetActive(false);rect.SetParent((Transform)GarbagePanel.GetInstance().garbageItemContent);});}else{//减时间GarbagePanel.GetInstance().SubtractTimer();//播放错误音效GarbagePanel.GetInstance().ErrorAudio();//垃圾回到原来的位置rect.anchoredPosition = originPos;//Debug.Log("错误");}}else{//垃圾回到原来的位置rect.anchoredPosition = originPos;}//拖拽结束 设置为空GarbagePanel.GetInstance().CurrentItem = null;//加分结束后,垃圾桶设置为空trash = null;}/// <summary>/// 停止dotween动画/// </summary>public void StopDoTween(){//dotween移动结束,移除自己GarbagePanel.GetInstance().MoveGarbageList.Remove(this);//停止并杀死动画,防止有残留tweener.Pause();tweener.Kill();//隐藏自己,并修改为1屏的父物体gameObject.SetActive(false);rect.SetParent(GarbagePanel.GetInstance().garbageItemContent);}}

五 程序包链接

链接:https://pan.baidu.com/s/1KAaM0GXSB9JtDyHHvC7-Yg?pwd=jl98 
提取码:jl98

Unity学习记录:制作双屏垃圾分类小游戏相关推荐

  1. c语言扔骰子随机数的相加,C语言编程学习:制作掷骰子小游戏

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  2. C语言编程学习:制作掷骰子小游戏!超适合新手的练手项目!

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  3. unity学习开发小球吃方块小游戏(从制作到发布程序,超详细全教程包你学会)

    unity小球吃方块开发过程 你将学会用unity开发小游戏的基础知识 吃方块游戏超详细全教程,保姆式教学包你学会 一.创建对象 创建PlayBall小球吃金币游戏项目,创建地面和小球. 二.添加脚本 ...

  4. 【游戏开发实战】使用Unity 2019制作仿微信小游戏飞机大战(七):主角飞机碰撞与爆炸

    文章目录 零.教程目录 一.前言 二.本篇目标 三.飞机机碰撞组件:BoxCollider2D.Rigidbody2D 四.添加Tag:Enemy 五.主角飞机碰撞处理:OnTriggerEnter2 ...

  5. 【抖音小游戏】 Unity制作抖音小游戏方案 最新完整详细教程来袭【持续更新】

    前言 [抖音小游戏] Unity制作抖音小游戏方案 最新完整详细教程来袭[持续更新] 一.相关准备工作 1.1 用到的相关网址 1.2 注册字节开发者后台账号 二.相关集成工作 2.1 下载需要的集成 ...

  6. c 语言掷骰子游戏,C 语言编程学习: 制作掷骰子小游戏

    C 语言编程学习: 制作掷骰子小游戏 问题描述 骰子是一个有六个面的正方体, 每个面分别印有 16 之间的小圆点代表点数. 假设这个游戏的规则是: 两个人轮流掷骰子 6 次, 并将每次投掷的点数累加起 ...

  7. Unity学习记录——模型与动画

    Unity学习记录--模型与动画 前言 ​ 本文是中山大学软件工程学院2020级3d游戏编程与设计的作业7 编程题:智能巡逻兵 1.学习参考 ​ 除去老师在课堂上讲的内容,本次作业代码与操作主要参考了 ...

  8. 基于深度学习的一款五子棋小游戏

    今天分享一个基于深度学习而开发的AI小游戏 简单介绍 这一款基于深度学习的五子棋小游戏的界面是使用Unity开发的,而网络结构是使用keras搭建的. 环境 笔者的环境如下 操作系统 windows ...

  9. C# 制作贪吃蛇小游戏,最简单的实现

    C# 制作贪吃蛇小游戏 目录 画蛇 实现蛇的上下左右移动 随机生成目标物 开始游戏 计分 重新开始 增加难度 死亡判定 1.1 画蛇的一节 Class Element()Graphics g;publ ...

最新文章

  1. 利用Windows Server Backup备份数据进行域控裸机还原
  2. 计算机网络-TCP拥塞控制
  3. UA MATH567 高维统计III 随机矩阵4 欧氏空间上的集网与覆盖
  4. C语言typedef和Windows数据类型
  5. 控制谷歌浏览器的最大化最小化
  6. java-通过JDBC操作数据库
  7. SQL笔试之盘古开天
  8. 访问:source.android.com和developer.android.com
  9. java对读写文件进行操作
  10. 我的第一个游戏FoodieThebug完成之后的心得体会--子龙山人
  11. win10 系统重装后,如何恢复mysql 数据库(mysql-8.0.26-winx64.zip)
  12. 卡方检验spss步骤_数据分析--学统计amp;SPSS操作
  13. c语言 音符符号大全,音符符号大全
  14. pip使用清华镜像源安装库
  15. 安卓系统目录说明大全,各文件夹是什么,可以删除吗?(以MIUI为例)
  16. android立体3D效果_3D立体画手绘墙体彩绘
  17. word文档被覆盖了怎么恢复原状
  18. 自媒体怎么赚钱?自媒体的5大变现方式!
  19. WebView(三)—— WebView使用漏洞
  20. 通讯业行业观察:中兴华为思科各占千秋

热门文章

  1. 全网最全抖音运营攻略
  2. des加密算法(des加密算法的密文分组长度)
  3. # 7-3 二分法求多项式单根
  4. 【数据结构复习】二叉树的遍历——从微软2014校园招聘说起
  5. Nginx 訪问日志增长暴增出现尖刀的具体分析
  6. 蓝桥 区别质因数,因数
  7. APP测试基本流程及APP测试要点
  8. 【课程设计】8086汇编实现打字小游戏
  9. 2021-2027全球与中国工业锂电池市场现状及未来发展趋势
  10. “狙击”特斯拉:电动汽车后起之秀的最后一战