Unity下Learn Scripting中的一个游戏教程学习。
------------------------------------------
1.准备工作
新建一个2D项目,命名为Quiz Game。
Assets下新建3个Folder,分别为Prefabs,Scenes,Scripts。删掉场景里的Main Camera,Ctrl + S保存该场景于Scenes Folder并命名为Persistent。
File→New Scene 命名为MenuScreen并保存于Scenes Folder。继续新建场景命名为Game并保存。
切换到Presistent场景新建空对象命名为DataController。Scripts Folder下新建两个C#脚本并命名为DataController和AnswerData。把DataController脚本挂到同名对象上。
2.创建数据类
打开AnswerData脚本并编辑
新建QuestionData C#脚本并编辑
新建RoundData C#脚本并编辑
打开DataController脚本并编辑
打开Build Settings把3个场景按下图顺序拖好后关闭Build Settings。这是为了表示加载场景的顺序。
然后把MenuScreen的Main Camera背景设为黑色。回到Presistent场景并播放测试,看到如脚本设置加载了MenuScreen场景,且把对象DataController设为不要摧毁。
3.设置游戏开始界面
在对象DataController的Inspector上编辑
新建Button命名为StartButton,按钮的text改为Start。Position和Pivot改为居中。
Canvas下新建Text命名为TilteText,内容改为Quiz Game,字号40,居中,放到合适的位置。
新建空对象命名为MenuScreenController,新建同名脚本并拖入。
编辑脚本
然后把MenuScreenController拖到StartButton并选取StartGame Function
4.游戏场景设置
切换到Game场景,新建Panel命名为QuesitonPanel设为不透明,背景为绿色,QuestionPanel下新建Text命名为QuestionDisplay,内容改为Question Text。字号为32,颜色为白色。Anchor Present为Middle Stretch(仅摁住Alt)。把锚点拉大一些,Top & Bottom复位为0,拉成合适的大小。
QuestionPanel下新建Panel命名为AnswerPanel并去掉Image组件,调整大小以和QuestionText并列,并添加Vertical Layout Group组件以约束之后要创建的答案按钮。(记得勾选Child Control Size的Wide & Height)
AnswerPanel下新建Button命名为AnswerButton并设Anchor Present居中(Alt ),其下Text的字号设为20
Canvas下新建Panel去掉Image组件并调整大小以到Question text上方,命名为UI。UI下新建Text命名为TimeDisplay,字号设为20,颜色为白,摁住Alt设为右上角,内容改为Time : 0。复制该Text命名为ScoreDisplay,把内容改成Score:0,然后移动复制的Text放到Time Text下方。
复制QuestionPanel删掉复制的AnswerPanel,背景色改为紫色。Text内容改为ROUND OVER。新建Button命名为MenuButton内容改为Menu。然后取消掉AnswerPanel的显示对号。把UI拖到最下方以显示在最上方。
Hierarchy下新建两个空对象,分别命名为GameController和AnswerButtonObjectPool。
5.答案按钮的设置
新建空对象命名为AnswerButtonObjectPool,新建C#脚本SimpleObjectPool,并拖入。这个对象是为了把所有带有不同内容的AnswerButton预制件做成一个ObjectPool并使用
在AnswerButton上新建C#命名为AnswerButton,打开并编辑
然后把AnswerButton按钮拖入Prefabs成预制件,并从Hierarchy上删除。然后把该预制件拖入到AnswerButtonObjectPool上的代码组件里
6.游戏流程控制GameController
GameController下添加同名C#脚本,打开并编辑
回到Unity,把所需选项拖到面板里
保存后我们播放测试,发现只加载到MenuScene,并没有顺利加载GameScene,且Console显示"NullReferenceException: Object reference not set to an instance of an object" Bug定位到没有正确的加载DataController,查找原因,是因为GameController里如下方式引用DataController。
查询API得知
实例化写在start()方法里面,而我又写的是用一个类去调用另一个类,而两个类中的变量都需要实例化,那么我在用A类去调用B类时,B类中的变量又可能还没有实例化,导致B类中的变量返回值为空。
故参考2D Roguelike游戏控制器设为单例的方式把代码改为
保存后再次播放测试,可顺利到达以下界面
7.使AnswerButton点击有效
AnswerButton预制件新建On Click 把挂在上面的同名脚本拖进去,选择HandleClick。
然后播放测试,出现和GameController一样的问题:没有引用。
更改脚本
8.结束该轮并重新开始游戏
在Game场景的MenuButton上新建点击事件,拖入Hierarchy下的GameController对象,选择里面的ReturnToMenu方法。保存后测试点击Menu就可以重新开始游戏了
9.用PlayerPrefs记录游戏最高分记录
新建C#脚本命名为PlayerProgress并编辑
Game场景下RoundPanel里新建Text命名为HighestScoreDisplay,并更改内容
打开DataController脚本并新增
打开GameController脚本并新增
保存后返回Unity并把HighScoreDisplay拖入
保存返回初始场景测试,最高分正常保存显示。
10.通过JSON加载游戏数据
在Assets文件夹下新建文件夹StreamingAssets,注意这个文件夹的名称要区分大小写,拼写要完全相同(case-sensitive)。这个文件夹下的文件我们在其他脚本内读取路径时不会随着工程文件夹(比如打包后)的移动而读取失败。新建记事本文件命名为data.json注意要改文件的后缀,然后保存在StreamingAssets下,打开并编辑。
上图是我为了阅读清晰加了缩进,实际上的文本是一行从头写到尾:
新建C#脚本GameData,打开并编辑
上图是错误的,如果使用
会报错:ArgumentException: Cannot deserialize JSON to new instances of type 'GameData'。
意思是不支持反序列化生成一个MonoBehaviour的子类。
正确的是:
然后打开DataController脚本并新增
保存后回到Unity测试,发现可正常读取JSON并运行。
另:
有时StreamingAssets会出现PC上正常,打包后无法读取的情况。是因为Build之后JSON被封装了,读取方式参考下面的链接里的作者答读者:
http://sammaru.blogspot.com/2017/03/unity-jsonutility.html
关于Unity内文件读取各路径及特点见下面链接:
http://topdevil.com/unity_path_ios_android/
11.自制一个可以编辑游戏数据的窗口(包含了保存游戏数据 为JSON& 从JSON中加载游戏数据并转换)
Scripts文件夹下新建文件夹命名为Editor,Editor文件夹下新建C#GameDataEditor,打开并编辑
保存后回到Unity打开Window→GameDataEditor可看到
可以在该界面里直接修改数据点Save data就可以保存新的数据为JSON并覆盖,可读取。
完毕。