《Anonia》游戏详细设计说明书
2022 年 8 月
开发团队:Chem Block
学校:大连理工大学
主要开发人:鄢世阳
目录
1 引言 ..................................................................................................................................................2
编写目的 ................................................................................................................................2
项目背景 ................................................................................................................................2
定义 ........................................................................................................................................3
参考资料 ................................................................................................................................4
2 总体设计 ..........................................................................................................................................4
游戏剧情概述 ........................................................................................................................4
软件结构 ................................................................................................................................4
3 程序描述 ..........................................................................................................................................5
界面模块 ................................................................................................................................5
角色创建模块 ......................................................................................................................30
交易模块 ..............................................................................................................................39
NPC 模块..............................................................................................................................40
敌人模块 ..............................................................................................................................59
保存模块 ..............................................................................................................................89
音乐系统模块 ......................................................................................................................91
测试要点 ..............................................................................................................................93
测试范围.....................................................................................................................93
测试方法.....................................................................................................................93
文档名称:《Anonia》游戏详细设计说明书
项目名称:Anonia 艾诺尼亚历险记
项目负责人:鄢世阳
编写鄢世阳 2015 年 8 月 30 日
校对所有小组成员 2015 年 9 月 1 日
审核所有小组成员 2015 年 9 月 2 日
批准刘苑星 2015 年 9 月 3 日
开发单位:大连理工大学 Chem Block 团队
组员: 鄢世阳何达威刘苑星刘佳鑫李旭
1 引言
编写目的
《Anonia》是一款开放式动作 RPG 游戏,它融合东西方奇幻世界背景,突破单一世界设定。本游戏以
清新亮丽的唯美风格为游戏画面,以国家之间的冲突为游戏基调,强调创造一个让玩家互动生活的虚拟世
界,鼓励玩家之间的交流、对抗,实现以玩家为游戏真正创造者的目的。《Anonia》游戏的设计初衷在于为
青少年高压快节奏的生活提供一个放松的平台,这个阶段的主要任务是在概要《Anonia》游戏设计书基础
上,对概要设计中产生的功能模块进行过程描述,设计功能模块的内部细节,包括算法和详细数据结构,
为编写源代码提供必要的说明。
概要设计解决了《Anonia》游戏总体结构设计的问题,包括整个游戏的结构、模块划分、模块功能和
模块间的联系等。详细设计则要解决如何实现各个模块的内部功能,即模块设计。具体的说,模块设计就
是要为已经设定的游戏模块的各子系统设计详细的算法,对实现细节作精确的描述。将模块划分为界面、
魔法、交易、战争等多个模块用算法和数据结构建立模块间的联系,保证了游戏的整体感。
项目背景
《Anonia》这款游戏是卡通渲染的方式展示一个爱恨交织的虚拟世界,他可以推进国内动漫游戏产业
的发展。国际动漫目前已形成了成熟的产业链,它首先通过卖动画的票房和电视播出收入作为第一块收入,
图书和音像收入为第二块,除了图书和音像制品以外,还有大量的衍生产品,主要体现在玩具、直销、商
品和主题公园几大块。动漫作为一个新兴的文化产业,从诞生之初就和高科技联系在一起,是高新技术发
展的产物。当今欧美、日、韩以强大的品牌力量,高水准的制作技术和健全的市场运作成为动漫产业的强国。
美国、日本和英国的动漫及其衍生产品已经成为国民经济中的支柱性产业,韩国以超常速度发展动漫产业,规
模仅次于美日。2004 年,全球数字内容产业产值预计将达 2228 亿美元,其中网络游戏业产值达 800 亿
美元,动画业产值达 450 亿美元,与游戏、动漫业相关的周边衍生产品产值则在 5000 亿美元以上。 1)
美国动漫产业。美国动画片经过长期的发展,形成鲜明的特点,在动画艺术上取得了辉煌的成就,也是最
初把动画片推向市场,并且形成产业规模的国家。迪斯尼公司最具代表性,到 2006 年已经制作了第 44 部
动画片,同时销售相关产品,形成一条完整的动漫产业链。迪斯尼公司在海外也不断扩张,香港迪斯尼乐
园已是全球第 11 个主题乐园。美国有 8 大动漫公司,是全世界最大的动漫国家,这个行业每年的收入超
过 1000 亿美元。 2)日本动漫产业。日本动画片在欧美、东南亚以及我国均占有较大的市场。目前全球
播放的动画节目约有 60%是日本制作的,漫画杂志和单行本的发行量已占杂志和图书发行总量的 45%,动
漫画的相互渗透,日本的电子游戏产品也得到了飞速的发展,由此衍生出动画电影、玩具等产品,创下近
1 万亿日元的产业规模,同时也扩大了日本文化在世界的影响力。 3)韩国动漫产业。韩国动漫产业在全
球的市场份额中不大,但发展势头强劲,2003 年产业规模仅为 3200 亿韩元,2007 年将实现 17000 亿韩
元的目标。手机游戏被列入国家 863 计划,2003 年中国手机游戏市场规模为 亿元,2004 年全年手机游
戏市场实现 亿元的产值.诺达咨询预计,随着 WAP 和 JAVA/BREW 等游戏业务的进一步成熟,2005 年国
内手机游戏市场规模达到 亿元,比 2004 年增长 %,2006 年手机游戏市场将达到 亿元,比 2005
年增长 %。2004 年,全球数字动漫产业的产值已达 2228 亿美元,与动漫产业相关的周边衍生产品产值
则在 5000 亿美元以上,英国数字娱乐产业年产值占 GDP 的 %,成为该国第一大产业。美国网络游戏业
已连续 4 年超过好莱坞电影业,成为全美最大娱乐产业。日本游戏市场每年创造 2 万亿日元市值规模,动
画产品出口值远远高于钢铁出口值。韩国动漫业产值占全球的 30%,已成为韩国国民经济的六大支柱产业
之一。从全球及主要国家来看,动漫产业已经成为一个庞大的产业。 2004 年我国动漫总创收达 117 亿元
人民币,国内动画片生产总量只有 万分钟,市场需求却在 万分钟,实际需求缺口达 23 万分钟,电
视台严重感到动画资源的不足。在中国青少年最喜爱的动漫作品中,日本动漫占 60%,欧美动漫占 29%,
而中国原创动漫,包括港台地区的比例只有 11%,明显落后于国外动漫产业的发展步伐,整个中国的动漫
产业仅处于初级发展阶段。在 2005 年上半年,各地兴起了建设动漫产业基地,在相关扶持政策的鼓励下,
投资动漫产业的高潮。
定义
•Mysql:数据库管理软件
•Unity3D:跨平台游戏开发工具
•Android:运行环境
•C#:软件开发语言
•Visual Studio 2015:编译工具
参考资料
《Unity3D\2D 手机游戏开发》金玺曾著
2 总体设计
游戏剧情概述
主人公从高处坠落,醒来时发现自己身处在一个被白雪覆盖的小镇,而自己是谁也不记得了。就这样,
主人公为了找回自己失去的记忆开始了一段奇妙的旅行。
软件结构
软件使用 Unity3D 进行的开发,运行平台是 Android,并支持跨平台到其他多个系统平台。
软件在 PC 端的构架是.Net Framework 。软件是以 scence 为基础,model 上附着刚体性质,并
与 script 想链接组成 Prefab。
3 程序描述
界面模块
底部物品栏
底部物品栏是手机 RPG 游戏的必备要素,我们使用 Unity3D 的 GUITexture
功能进行编写。代码如下:
using UnityEngine;
using ;
public class BottomBar : MonoBehaviour {
private Vector2 defaultScreenRes; //Screen Resolution
[]
public class GUISetting
{
public Vector2 position;
public Vector2 size;
public Texture2D[] texture;
}
[]
public class LabelSetting
{
public Vector2 position;
public GUIStyle labelStyle;
}
[HideInInspector]
public float currentCastTime;
[HideInInspector]
public float castTime;
[HideInInspector]
public bool showCastBar;
public GUISetting expBar; //exp bar setting
public LabelSetting expText; //exp text setting
public GUISetting castBar; //cast bar setting
private PlayerStatus playerStatus;
// Use this for initialization
void Start () {
= 1920; //declare max screen ratio
= 1080; //declare max screen ratio
GameObject go = ("Player");
//Find player
playerStatus = <PlayerStatus>();
}
void OnGUI () {
// Resize GUI Matrix according to screen size
ResizeGUIMatrix();
if(!)
{
//Cast bar
if(showCastBar)
{
(new Rect(,
,,));
(new Rect(0,0,
,), [0]);
(newRect(0,0,Convert(,castTime,currentCastTime
),));
(new Rect(0,0,,), [1]);
();
();
}
//Exp bar
(new Rect(,
,,));
(new Rect(0,0,
,), [0]);
(new
Rect(0,0,Convert(,,),expBar.
));
(new
Rect(0,0,,), [1]);
();
();
//Exp text
(new
Rect( ,, 1000 , 1000)
,().ToString() + "/" +
().ToString(),,,C
,2f);
}
// Reset matrix after finish
= ;
}
void ResizeGUIMatrix()
{
// Set matrix
Vector2 ratio = new Vector2( ,
);
Matrix4x4 guiMatrix = ;
(new Vector3(1, 1, 1), , new
Vector3(, , 1));
= guiMatrix;
}
float Convert(float maxWidthGUI, float maxValue, float curValue)
//Calculate hp bar-mp bar
{
float val = maxWidthGUI/maxValue;
float load = curValue*val;
return load;
}
}
命令菜单
命令菜单的制作也是用了 Unity3D 的 OnGUI 函数。
代码如下:
using UnityEngine;
using ;
public class CommandMenu : MonoBehaviour {
private Vector2 defaultScreenRes; //Screen Resolution
[]
public class ButtonSetting
{
public Vector2 position;
public Vector2 size;
public GUIStyle buttonStlye;
}
public ButtonSetting status,bag,equip,skill; //button setting
// Use this for initialization
void Start () {
= 1920; //declare max screen ratio
= 1080; //declare max screen ratio
}
// Update is called once per frame
void OnGUI () {
// Resize GUI Matrix according to screen size
ResizeGUIMatrix();
if(!)
{
//when click button status
if((new
Rect(,,,),"",
))
{
("Status");
}
//when click button inventory
if((new
Rect(,,,),"",))
{
("Inventory");
}
//when click button equipment
if((new
Rect(,,,),"",)
)
{
("Equipment");
}
//when click button skill
if((new
Rect(,,,),"",))
{
("Skill");
}
}
// Reset matrix after finish
= ;
}
void ResizeGUIMatrix()
{
// Set matrix
Vector2 ratio = new Vector2( ,
);
Matrix4x4 guiMatrix = ;
(new Vector3(1, 1, 1), , new
Vector3(, , 1));
= guiMatrix;
}
}
死亡窗口
角色死亡时会显示死亡窗口,代码如下:
using UnityEngine;
using ;
public class DeadWindow : MonoBehaviour {
[]
public class GUISetting
{
public Vector2 position;
public Vector2 size;
public Texture2D texture;
}
[]
public class ButtonSetting
{
public Vector2 position;
public Vector2 size;
public GUIStyle buttonStlye;
}
private Vector2 defaultScreenRes; //Screen Resolution
public GUISetting cautionWindow; //window setting
public ButtonSetting buttonReturn,buttonQuit; //button setting
public string sceneQuitGame; //name scene quit when you select quit button
//Private variable
private HeroController controller;
public static bool enableWindow; //check enable disable window
// Use this for initialization
void Start () {
= 1920; //declare max screen ratio
= 1080; //declare max screen ratio
GameObject go = ("Player");
//Find player
controller = <HeroController>();
}
void OnGUI()
{
// Resize GUI Matrix according to screen size
ResizeGUIMatrix();
if(enableWindow)
{
//draw window
(new
Rect(,,,cau
),);
//when click return
if((new
Rect(,,,buttonRetur
),"",))
{
();
}
//when click quit game
if((new
Rect(,,,),
"",))
{
Invoke("LoadScene",);
}
}
// Reset matrix after finish
= ;
}
void ResizeGUIMatrix()
{
// Set matrix
Vector2 ratio = new Vector2( ,
);
Matrix4x4 guiMatrix = ;
(new Vector3(1, 1, 1), , new
Vector3(, , 1));
= guiMatrix;
}
void LoadScene()
{
(sceneQuitGame);
enableWindow = false;
}
}
任务系统界面
作为 rpg 游戏里必须有的任务系统,代码如下:
using UnityEngine;
using ;
public class QuestWindow : MonoBehaviour {
[]
public class GUISetting
{
public Vector2 position;
public Vector2 size;
public Texture2D texture;
}
[]
public class ButtonSetting
{
public Vector2 position;
public Vector2 size;
public GUIStyle buttonStlye;
}
[]
public class LabelSetting
{
public Vector2 position;
public GUIStyle style;
public bool enableStroke;
public Color strokeColor;
}
private Vector2 defaultScreenRes; //Screen Resolution
public GUISetting questWindow; //window setting
public ButtonSetting buttonAccept,buttonCancel; //button setting
public LabelSetting
titleFont,detailFont,rewardLabel,itemNameFont,targetLabel,targetFont;
[HideInInspector]
public HeroController controller; //script controller
public static bool enableWindow; //check enable/disable window
public static bool enableButtonAccept; // check enable/disable accept button
private Quest_Data questData;
private Item_Data itemData;
private Monster_Data monsterData;
[Multiline]
public static string detailQuest;
public static string titleQuest;
public static Vector2 condition;
public static Vector2 reward;
private int questID;
private string itemRewardName;
private string conditionName;
// Use this for initialization
void Start () {
enableWindow = false;
= 1920; //declare max screen ratio
= 1080; //declare max screen ratio
GameObject go = ("Player");
//Find player
controller = <HeroController>();
questData =
("QuestData").GetComponent<Quest_Data>();
itemData =
("Item_Data").GetComponent<Item_Data>();
monsterData =
("MonsterData").GetComponent<Monster_Data>();
}
void OnGUI()
{
// Resize GUI Matrix according to screen size
ResizeGUIMatrix();
if(enableWindow)
{
if(!)
= true;
//Draw window
(new
Rect(,,,questWin
),);
if(enableButtonAccept)
{
//Draw accept button
if((new
Rect(,,,buttonAcc
),"",))
{
("Accept_Quest");
= true;
= false;
= false;
= false;
= false;
(questID);
= false;
= false;
enableButtonAccept = false;
enableWindow = false;
}
//Draw cancel button
if((new
Rect(,,,buttonCanc
),"",))
{
= true;
= false;
= false;
= false;
= false;
= false;
enableButtonAccept = false;
enableWindow = false;
}
}
//Title Quest
if()
(new
Rect( ,, 1000 , 1000)
,titleQuest,,,,2f)
;
else
(new Rect( ,,
1000 , 1000),titleQuest,);
//Detail Quest
if()
(new
Rect( ,, 1000 , 1000)
,detailQuest,,,
olor,2f);
else
(new
Rect( ,, 1000 ,
1000),detailQuest,);
//Target Label
if()
(new
Rect( ,, 1000 , 1000)
,"Target",,,
olor,2f);
else
(new
Rect( ,, 1000 ,
1000),"Target",);
//Target
if()
(new
Rect( ,, 1000 , 1000)
,() + " x " +
conditionName,,,
lor,2f);
else
(new
Rect( ,, 1000 , 1000),()
+ " x " + conditionName,);
//Reward Label
if()
(new
Rect( ,, 1000 , 1000)
,"Reward",,,.
textColor,2f);
else
(new
Rect( ,, 1000 ,
1000),"Reward",);
//Reward
if()
(new
Rect( ,, 1000 , 1000)
,() + " x " +
itemRewardName,,,
,2f);
else
(new
Rect( ,, 1000 ,
1000),() + " x " + itemRewardName,);
}
// Reset matrix after finish
= ;
}
void ResizeGUIMatrix()
{
// Set matrix
Vector2 ratio = new Vector2( ,
);
Matrix4x4 guiMatrix = ;
(new Vector3(1, 1, 1), , new
Vector3(, , 1));
= guiMatrix;
}
public void SetupQuestWindow(int id)
{
for(int i=0;i<;i++)
{
if(id == [i].questID)
{
titleQuest = [i].questName;
detailQuest = [i].questDetail;
= [i].;
= [i].;
= [i].;
= [i].;
if([i].questCondition ==
)
ConvertItemIDToName((int),"Condition");
else ConvertEnemyIDToName((int));
ConvertItemIDToName((int),"Reward");
questID = id;
}
}
}
void ConvertEnemyIDToName(int enemyID)
{
//Check Monster Id
for(int i=0;i < ;i++)
{
if(enemyID == [i].enemyID)
{
conditionName =
[i].;
break;
}
}
}
void ConvertItemIDToName(int itemID,string type)
{
//Check Usable Item
for(int i=0;i < ;i++)
{
if(itemID == _usable_set[i].item_ID)
{
if(type == "Condition")
{
conditionName = _usable_set[i].item_Name;
}
else if(type == "Reward")
{
itemRewardName =
_usable_set[i].item_Name;
}
break;
}
}
//Check Weapon
for(int i=0;i < ;i++)
{
if(itemID == _equipment_set[i].item_ID)
{
if(type == "Condition")
{
conditionName =
_equipment_set[i].item_Name;
}
else if(type == "Reward")
{
itemRewardName =
_equipment_set[i].item_Name;
}
break;
}
}
//Check Etc Item
for(int i=0;i < ;i++)
{
if(itemID == _etc_set[i].item_ID)
{
if(type == "Condition")
{
conditionName = _etc_set[i].item_Name;
}
else if(type == "Reward")
{
itemRewardName = _etc_set[i].item_Name;
}
break;
}
}
//Check Gold
for(int i=0;i < ;i++)
{
if(itemID == _gold[i].item_ID)
{
if(type == "Condition")
{
conditionName = _gold[i].item_Name;
}
else if(type == "Reward")
{
itemRewardName = _gold[i].item_Name;
}
break;
}
}
}
}
角色创建模块
角色选择
我设计两种职业可以选择,法师与剑士。法师拥有更高的魔法量,擅长远程
攻击。剑士拥有更高的血量与防御力,擅长近战攻击。为了增加游戏的丰富度,
我们设计了姓名输入框,让玩家可以为自己喜欢的角色起好听的名字,增加游戏
的 自 由 度 。
在角色选择界面上,我们使用另开一个 scence 来进行界面表达,代码如下:
using UnityEngine;
using ;
public class SelectCharacter : MonoBehaviour {
public GameObject[] hero; //your hero
public GameObject buttonNext,buttonPrev; //button prev and button next
[HideInInspector]
public int indexHero = 0; //index select hero
private GameObject[] heroInstance; //use to keep hero gameobject when
Instantiate
// Use this for initialization
void Start () {
heroInstance = new GameObject[]; //add array size equal
hero size
indexHero = 0; //set default selected hero
SpawnHero(); //spawn hero to display current selected
//check if hero is less than 1 , button next and prev will disappear
if( <= 1)
{
(false);
(false);
}
}
//Check show only selected character
public void UpdateHero(int _indexHero)
{
for(int i=0; i<;i++)
{
//Show only select character
if(i == _indexHero)
{
heroInstance[i].SetActive(true);
}else
{
//Hide Other Character
heroInstance[i].SetActive(false);
}
}
}
//Spawn all hero
public void SpawnHero()
{
for(int i=0;i<;i++)
{
heroInstance[i] =
(GameObject)Instantiate(hero[i],,);
}
UpdateHero(indexHero);
}
}
输入名字
代码如下:
using UnityEngine;
using ;
public class Entername : MonoBehaviour {
public string defaultName; //Default name it will show when you not input
anything
public Vector2 posEnterName; //position enter name box
public Vector2 sizeEnterName; //size enter name box
private Vector2 defaultScreenRes; //Screen Resolution
public GUIStyle enterNameStyle; //enter name style
// Use this for initialization
void Start () {
= 1920; //declare max screen ratio
= 1080; //declare max screen ratio
}
// Update is called once per frame
void OnGUI () {
// Resize GUI Matrix according to screen size
ResizeGUIMatrix();
// draw enter name box
defaultName = (new
Rect(,,,),defaultNa
me,enterNameStyle);
// Reset matrix after finish
= ;
}
void ResizeGUIMatrix()
{
// Set matrix
Vector2 ratio = new Vector2( ,
);
Matrix4x4 guiMatrix = ;
(new Vector3(1, 1, 1), , new
Vector3(, , 1));
= guiMatrix;
}
}
选择按钮
using UnityEngine;
using ;
public class ButtonSelected : MonoBehaviour {
public string buttonName; //button name
public Texture2D buttonOkNormal; //Texture button normal
public Texture2D buttonOkDown; //Texture button down
public SelectCharacter selectCharacterScript;//script select character
public AudioClip sfxButton; //sound effect when click this button
void OnMouseUp()
{
//change texture to "normal"
= buttonOkNormal;
//if button name = Next when click this button, Choose next character
if(buttonName == "Next")
{
+= 1;
if( >=
)
{
= 0;
}
();
}
//if button name = Prev when click this button, Choose previous
character
if(buttonName == "Prev")
{
-= 1;
if( < 0)
{
=
-1;
}
();
}
}
void OnMouseDown()
{
//change texture to "down"
= buttonOkDown;
//Play sfx
(sfxButton,);
}
}
确认按钮
代码如下:
using UnityEngine;
using ;
public class ButtonOK : MonoBehaviour {
public string loadSceneName; //Next load scene name
public Texture2D buttonOkNormal; //Texture button normal
public Texture2D buttonOkDown; //Texture button down
public Entername enterNameScript; //script entername
public SelectCharacter selectCharacterScript; //script select character
public AudioClip sfxButton; //sound effect when click this button
void OnMouseUp()
{
//change texture to "normal"
= buttonOkNormal;
//Save select character and name
("pName",);
("pSelect",);
= true;
//Load next scene
Invoke("LoadScene",);
}
void OnMouseDown()
{
//change texture to "down"
= buttonOkDown;
//Play sfx
if(sfxButton != null)
(sfxButton,);
}
void LoadScene()
{
(loadSceneName);;
}
}
交易模块
代码如下:using UnityEngine;
using ;
using ;
public class ShopItemlist : MonoBehaviour {
public List<int> itemID = new List<int>();
void Start()
{
if( == "Untagged")
= "Npc_Shop";
}
}
NPC 模块
代码如下:
using UnityEngine;
using ;
using ;
public class NpcSetup : MonoBehaviour {
public enum NameTagSetup {NpcName,PlayerName,Other};
public enum NpcType {RegularNpc,ShopKeeper,QuestNpc,SaveNpc};
public enum FaceSetup {NpcFace,PlayerFace,Other};
public enum
QuestNpcState{StartQuest,InProgress,Complete,AfterComplete};
public NpcType npcType;
public string npcName;
public Texture2D npcFace;
private bool startCountDialogBox;
[]
public class MessageBoxSetting
{
public bool enableNameTag = true;
public NameTagSetup nameTagSetup;
public string otherNameTag;
public bool enableFace = true;
public FaceSetup faceSetup;
public Texture2D otherFace;
[Multiline]
public string message;
}
public List<MessageBoxSetting> dialogSetting = new
List<MessageBoxSetting>();
public int questID;
public QuestNpcState questNpcState;
public List<MessageBoxSetting> dialogQuest = new
List<MessageBoxSetting>();
public List<MessageBoxSetting> dialogQuestInProgress = new
List<MessageBoxSetting>();
public List<MessageBoxSetting> dialogQuestComplete = new
List<MessageBoxSetting>();
public List<MessageBoxSetting> dialogQuestAfterComplete = new
List<MessageBoxSetting>();
private HeroController playerControl;
private int indexDialog;
private int indexCurrentQuest;
private bool disableMovePlayer;
private Quest_Data questData;
private QuestWindow questWindow;
public GUI_Menu inventory;
private GameObject player;
public static bool resetMessageBox;
public static bool disableNext;
//Editor Variable
[HideInInspector]
public int sizeDialog=0;
[HideInInspector]
public int sizeDialogQuest=0;
[HideInInspector]
public int sizeDialogQuestInProgress=0;
[HideInInspector]
public int sizeDialogQuestComplete=0;
[HideInInspector]
public int sizeDialogQuestAfterComplete=0;
[HideInInspector]
public List<bool> showSizeDialog = new List<bool>();
[HideInInspector]
public List<bool> showSizeDialogQuest = new List<bool>();
[HideInInspector]
public List<bool> showSizeDialogQuestInProgress = new List<bool>();
[HideInInspector]
public List<bool> showSizeDialogQuestComplete = new List<bool>();
[HideInInspector]
public List<bool> showSizeDialogQuestAfterComplete = new List<bool>();
// Use this for initialization
void Start () {
// Change this tag to Npc
if( != "Npc")
= "Npc";
if( != 13)
= 13;
indexDialog = 0;
playerControl =
("Player").GetComponent<HeroController>();
if(npcType == )
{
questData =
("QuestData").GetComponent<Quest_Data>();
questWindow = ("GUI
Manager/QuestWindow").GetComponent<QuestWindow>();
player = ("Player");
inventory =
("Inventory").GetComponent<GUI_Menu>();
}
}
// Update is called once per frame
void Update () {
if(npcType == || npcType ==
|| npcType == )
NpcDetect();
else if(npcType == )
QuestNpcDetect();
}
void NpcDetect()
{
if(disableMovePlayer)
DisableMove();
if(startCountDialogBox)
{
SetupDialogBox(indexDialog);
if((0) ||
() || ())
{
("Next_Dialog");
if(indexDialog < -1)
{
indexDialog++;
}else
{
if(npcType == || npcType ==
)
CallNextCommand();
indexDialog = 0;
startCountDialogBox = false;
= false;
= false;
= false;
Invoke("EnableMovePlayer",);
}
}
}
}
void QuestNpcDetect()
{
if(disableMovePlayer)
DisableMove();
if(startCountDialogBox)
{
if(questNpcState == )
SetupDialogBoxQuest(indexDialog,dialogQuest);
else if(questNpcState == )
SetupDialogBoxQuest(indexDialog,dialogQuestInProgress);
else if(questNpcState == )
SetupDialogBoxQuest(indexDialog,dialogQuestComplete);
else if(questNpcState == )
SetupDialogBoxQuest(indexDialog,dialogQuestAfterComplete);
if(questNpcState == )
{
if((0) ||
() || () ||
resetMessageBox)
{
if(!)
("Next_Dialog");
if(indexDialog < -1)
{
indexDialog++;
if(indexDialog == -1)
{
= true;
= true;
disableNext = true;
(questID);
}
}else if(!disableNext)
{
disableNext = false;
indexDialog = 0;
startCountDialogBox = false;
= false;
= false;
= false;
resetMessageBox = false;
Invoke("EnableMovePlayer",);
}
}
}else
if((0) ||
() || ())
{
("Next_Dialog");
if(questNpcState == )
{
if(indexDialog < -1)
{
indexDialog++;
}else
{
indexDialog = 0;
startCountDialogBox = false;
= false;
= false;
= false;
= false;
Invoke("EnableMovePlayer",);
}
}
else if(questNpcState == )
{
if(indexDialog < -1)
{
indexDialog++;
}else
{
if(![indexCurrentQuest].repeatQuest)
{
[indexCurrentQuest].questState = 3;
}else
{
[indexCurrentQuest].questState = 0;
}
GiveReward(questID);
indexDialog = 0;
startCountDialogBox = false;
= false;
= false;
= false;
Invoke("EnableMovePlayer",);
}
}
else if(questNpcState == )
{
if(indexDialog < -1)
{
indexDialog++;
}else
{
indexDialog = 0;
startCountDialogBox = false;
= false;
= false;
= false;
Invoke("EnableMovePlayer",);
}
}
}
}
}
public void DisableMove()
{
= true;
= true;
}
public void CallDialogBox()
{
startCountDialogBox = true;
= true;
disableMovePlayer = true;
}
void EnableMovePlayer()
{
disableMovePlayer = false;
();
}
void SetupDialogBox(int i)
{
if(dialogSetting[i].enableNameTag)
{
if(dialogSetting[i].nameTagSetup == )
=
("Player").GetComponent<PlayerStatus>().pla
yerName;
else if(dialogSetting[i].nameTagSetup ==
)
= npcName;
else if(dialogSetting[i].nameTagSetup == )
= dialogSetting[i].otherNameTag;
= true;
}else
{
= false;
}
if(dialogSetting[i].enableFace)
{
if(dialogSetting[i].faceSetup == )
=
("Player").GetComponent<HeroController>().
heroImage;
else if(dialogSetting[i].faceSetup == )
= npcFace;
else if(dialogSetting[i].faceSetup == )
= dialogSetting[i].otherFace;
= true;
}else
{
= false;
}
= dialogSetting[i].message;
}
void SetupDialogBoxQuest(int i,List<MessageBoxSetting> mBoxQuest)
{
if(mBoxQuest[i].enableNameTag)
{
if(mBoxQuest[i].nameTagSetup == )
=
("Player").GetComponent<PlayerStatus>().pla
yerName;
else if(mBoxQuest[i].nameTagSetup == )
= npcName;
else if(mBoxQuest[i].nameTagSetup == )
= mBoxQuest[i].otherNameTag;
= true;
}else
{
= false;
}
if(mBoxQuest[i].enableFace)
{
if(mBoxQuest[i].faceSetup == )
=
("Player").GetComponent<HeroController>().
heroImage;
else if(mBoxQuest[i].faceSetup == )
= npcFace;
else if(mBoxQuest[i].faceSetup == )
= mBoxQuest[i].otherFace;
= true;
}else
{
= false;
}
= mBoxQuest[i].message;
}
void CallNextCommand()
{
if(npcType == )
{
();
}else if(npcType == )
{
}else if(npcType == )
{
();
}
}
public void SetupDialogQuest(int id)
{
for(int i=0;i<;i++)
{
if(questID == [i].questID)
{
if([i].questState == 1)
CheckConditionQuest(id);
if([i].questState == 0)
{
questNpcState = ;
if(indexDialog == -1)
{
= true;
= true;
disableNext = true;
(questID);
}
}else if([i].questState == 1)
{
questNpcState = ;
(questID);
= true;
}else if([i].questState == 2)
{
questNpcState = ;
}else if([i].questState == 3)
{
questNpcState = ;
}
indexCurrentQuest = i;
break;
}
}
}
void CheckConditionQuest(int id)
{
for(int i=0;i<;i++)
{
if(questID == [i].questID)
{
if([i].questCondition ==
)
{
if([i].killCount >=
(int)[i].)
{
[i].killCount = 0;
[i].questState = 2;
}
}else
{
if(CheckConditionItem((int)[i].,(int)questDat
[i].))
{
[i].killCount = 0;
[i].questState = 2;
}
}
break;
}
}
}
bool CheckConditionItem(int id,int amout)
{
for(int i =0;i<;i++)
{
if(_item[i].item_id == id &&
_item[i].item_amount >= amout)
{
_item[i].item_amount -= amout;
if(_item[i].item_amount <= 0)
{
_item[i].item_id = 0;
}
return true;
}
}
return false;
}
void GiveReward(int id)
{
for(int i=0;i<;i++)
{
if(questID == [i].questID)
{
((int)[i].,(int)questDat
[i].);
("Give");
break;
}
}
}
}
敌人模块
代码如下:
敌人状态
代码如下:using UnityEngine;
using ;
public class EnemyStatus : MonoBehaviour {
public string enemyName;
public int enemyID;
[]
public class Attribute
{
public int hp,mp,atk,def,spd,hit;
public float criticalRate,atkSpd,atkRange,movespd;
}
public Attribute status;
public int expGive;
}
领地保护
using UnityEngine;
using ;
public class DetectArea : MonoBehaviour {
[HideInInspector]
public EnemyController controller;
public bool drawGizmos;
// Use this for initialization
void Start () {
controller = <EnemyController>();
}
void OnTriggerEnter(Collider c)
{
if( == "Player")
{
= true;
= ;
}
}
void OnTriggerExit(Collider c)
{
if( == "Player")
{
= false;
= null;
}
}
//Draw Gizmoz
[ExecuteInEditMode]
void OnDrawGizmosSelected()
{
if(drawGizmos)
{
= new Color(1f,0f,0f,);
(,<SphereCollider>().r
adius);
}
}
}
敌人控制
代码如下:using UnityEngine;
using ;
using ;
[RequireComponent (typeof (AnimationManagerEnemy))]
[RequireComponent (typeof (EnemyStatus))]
[RequireComponent (typeof (CharacterController))]
[RequireComponent (typeof (DropItem))]
public class EnemyController : MonoBehaviour {
public enum ControlAnimationState
{Idle,Move,WaitAttack,Attack,TakeAtk,Death}; //enemy state
public enum EnemyBehavior {Standing,MoveAround} //enemy behavior
public enum EnemyNature {Natural,Wild} //enemy nature
public enum MoveAroundBehavior {MoveToNext,Waiting}
public EnemyBehavior behavior; //enemy behavior
public EnemyNature nature; //enemy nature
public MoveAroundBehavior moveBehavior; //movement behavior
public GameObject target; //Target enemy
public bool chaseTarget; //Chase Enemy
public List<GameObject> modelMesh = new List<GameObject>(); //model
mesh
public Color colorTakeDamage; //color model when take attack
public float deadTimer; //destroy timer when enemy dead
public bool deadTransperent; //if enable this when enemy dead it will
transperent
public float speedFade; //speed transperent
public bool regenHP; //regen hp when enemy return to spawn point
public bool regenMP; //regen mp when enemy return to spawn point
public ControlAnimationState ctrlAnimState; //Control Animation State
public float movePhase; //area enemy move
public float returnPhase; //range when enemy return to spawn point
public float delayNextTargetMin; //delay when idle - min
public float delayNextTargetMax; //delay when idel - max
//Private variable
//Get component other script
private AnimationManagerEnemy animationManager;
private EnemyStatus enemyStatus;
private Quest_Data questData;
private CharacterController controller;
private float defaultReturnPhase; //default return phase
private Vector3 spawnPoint; //first spawn point
private float timeToWaitBeforeNextMove; //Time to wait before go to next
target
private float delayAttack = 100; //Delay Attack speed
private Vector3 destinationPosition; // The destination Point
private float destinationDistance; // The distance between
and destinationPosition
private Vector3 movedir;
private float moveSpeed; // The Speed the character
will move
private Vector3 ctargetPos; //Convert Target Position
private Vector3 targetPos; //Target Pos
private Quaternion targetRotation; //Rotation
private bool checkCritical; //Check Critical
private float flinchValue = 100; //Check Enemy
flinch
private Vector3 randomMoveVector; //Randome Move Position
private float randomAngle; //Random Angle Move
private Shader alphaShader; //Use to transperent model
private Color[] defaultColor; //Default Material Color
private bool enableRegen; //Check Regen HP/MP
private GameObject detectArea; //get detectArea if natural is
wild
private float fadeValue; // Fade Value
[HideInInspector]
public float defaultHP,defaultMP; //DefaultHP,MP
[HideInInspector]
public int typeAttack; //Type Attack
[HideInInspector]
public int typeTakeAttack; //Type TakeAttack
[HideInInspector]
public bool startFade; //Check setup fade material
//Editor Variable
[HideInInspector]
public int sizeMesh;
// Use this for initialization
void Start () {
//set spawn point
destinationPosition = ;
//get anathor component
animationManager = <AnimationManagerEnemy>();
enemyStatus = <EnemyStatus>();
controller = <CharacterController>();
questData =
("QuestData").GetComponent<Quest_Data>();
delayAttack = 100; //Declare delay 100 sec
flinchValue = 100; //Declare flinch value (if zero it will flinch)
fadeValue = 1; //Set fade value
//Set first spawn point
spawnPoint = ;
//set default value
defaultReturnPhase = returnPhase;
defaultHP = ;
defaultMP = ;
defaultColor = new Color[];
SetDefualtColor();
if(behavior == )
{
moveBehavior = ;
}
//warning when enemy isn't detect area
if(nature == )
{
detectArea = ("DetectArea");
if(detectArea == null)
{
("Don't found DetectArea in Enemy -" +
);
}
}
}
// Update is called once per frame
void Update () {
EnemyAnimationState();
if(ctrlAnimState != )
{
if(behavior == && !chaseTarget
&& !target)
{
if (moveBehavior == )
{
UpdateMovingToNextPatrolPoint();
}
else if (moveBehavior == )
{
UpdateWaitingForNextMove();
}
}else
{
EnemyMovementBattle();
}
}
}
//State Enemy
void EnemyAnimationState() {
if(ctrlAnimState == )
{
= ;
}
if(ctrlAnimState == )
{
= ;
}
if(ctrlAnimState == )
{
= ;
WaitAttack();
}
if(ctrlAnimState == )
{
LookAtTarget();
if(checkCritical)
{
=
;
delayAttack = 100;
}else if(!checkCritical)
{
= ;
delayAttack = 100;
}
}
if(ctrlAnimState == )
{
= ;
}
if(ctrlAnimState == )
{
if( == "Enemy")
{
= "Untagged";
<DropItem>().UseDropItem();
}
= ;
if(startFade)
{
DeadTransperentAlpha(speedFade);
}
}
}
//randomw move position
void RandomPostion()
{
randomAngle = (0f,91);
= (randomAngle) * movePhase +
;
= (randomAngle) * movePhase +
;
= ;
targetRotation = (randomMoveVector -
);
destinationPosition = randomMoveVector;
}
//move to next target
void UpdateMovingToNextPatrolPoint()
{
destinationDistance =
(destinationPosition,); //Check Distance
Spawnpoint to Enemy
if (destinationDistance < 1f)
{
timeToWaitBeforeNextMove =
(delayNextTargetMin, delayNextTargetMax);
moveBehavior = ;
ctrlAnimState = ;
moveSpeed = 0;
}
// Move to destination
if(ctrlAnimState == ){
=
(,targetRotation, *25);
if(){
movedir = ;
movedir =
(*moveSpeed);
}
}else
{
movedir = (movedir,, *
10);
}
-= 20 * ;
(movedir*);
}
//wait for next move
void UpdateWaitingForNextMove()
{
timeToWaitBeforeNextMove -= ;
if (timeToWaitBeforeNextMove < )
{
RandomPostion();
moveBehavior = ;
ctrlAnimState = ;
moveSpeed = ;
}
}
//Enemy movement when enemy found target
void EnemyMovementBattle()
{
if(chaseTarget == true)
{
LookAtTarget();
destinationDistance = (,
); //Check Distance Enemy to Hero
if(destinationDistance <= ){ //
Reset speed to 0
if(ctrlAnimState == ||
ctrlAnimState == )
ctrlAnimState = ;
moveSpeed = 0;
}
else if(destinationDistance > ){
// To Reset Speed to default
if(ctrlAnimState == ||
ctrlAnimState == || ctrlAnimState ==
)
ctrlAnimState = ;
moveSpeed = ;
}
}else if(chaseTarget == false)
{
LookAtTarget(spawnPoint);
destinationDistance = (spawnPoint,
); //Check Distance Enemy to Hero
if(destinationDistance <= ){ //
Reset speed to 0
if(ctrlAnimState == ||
ctrlAnimState == )
ctrlAnimState = ;
moveSpeed = 0;
returnPhase = defaultReturnPhase;
if(enableRegen)
{
if(regenHP)
= (defaultHP);
if(regenMP)
=
(defaultMP);
destinationPosition = ;
target = null;
}
}
else if(destinationDistance > ){
// To Reset Speed to default
if(ctrlAnimState == ||
ctrlAnimState == || ctrlAnimState ==
)
ctrlAnimState = ;
moveSpeed = ;
}
}
//Check distance spawn
float distanceSpawn = (spawnPoint,
);
if(distanceSpawn > returnPhase)
{
if(regenHP || regenMP)
enableRegen = true;
chaseTarget = false;
returnPhase += 3;
}
// Move to destination
if(ctrlAnimState == ){
if(){
movedir = ;
movedir =
(*moveSpeed);
}
}else
{
movedir = (movedir,, *
10);
}
-= 20 * ;
(movedir*);
}
//Wait before attack
void WaitAttack()
{
PlayerStatus playerStatus;
playerStatus = <PlayerStatus>();
if( <= 0)
{
ResetState();
}
if(delayAttack > 0)
{
delayAttack -= * ;
}else if(delayAttack <= 0)
{
checkCritical = CriticalCal();
if(checkCritical)
{
typeAttack =
(0,);
= false;
}else if(!checkCritical)
{
typeAttack =
(0,);
= false;
}
ctrlAnimState = ;
}
}
//Look at target method
void LookAtTarget(Vector3 _targetPos)
{
= ;
= ;
= ;
(targetPos);
}
//Critical Calculate
bool CriticalCal(float criticalStat)
{
float calCritical = criticalStat - (0,101f);
if(calCritical > 0)
{
return true; //Critical
}else
{
return false; //Not Critical
}
}
//Get damage method
public void GetDamage(float targetAttack,float targetHit,float
flichRate,GameObject atkEffect,AudioClip atksfx)
{
//Calculate Hit
targetHit += (-10,30);
//Random take damage animation
typeTakeAttack =
(0,);
if( - targetHit > 0) //Attack Miss
{
("Attack_Miss");
InitTextDamage(,"Miss");
//Lock Target
if(!chaseTarget)
chaseTarget = true;
if(ctrlAnimState == || ctrlAnimState ==
)
ctrlAnimState = ;
}else
{
int damage = ((targetAttack -
) * (,));
if(damage <= 5)
{
damage = (1,11); // if def < enemy attack
}
//Play SFX
if(atksfx)
(atksfx,);
//Spawn Effect
if(atkEffect)
Instantiate(atkEffect,,);
InitTextDamage(,());
-= damage;
GetDamageColorReset();
if( <= 0)
{
= 0;
//Reset State Hero when kill this enemy
if(target)
{
HeroController enemy;
enemy = <HeroController>();
();
PlayerStatus pStatus;
pStatus = <PlayerStatus>();
+= ;
AddKillCountQuest();
}
Destroy(,deadTimer);
ctrlAnimState = ;
}else
{
if(!chaseTarget)
chaseTarget = true;
flinchValue -= flichRate;
if(flinchValue <= 0)
{
ctrlAnimState = ;
flinchValue = 100;
}else
{
ctrlAnimState = ;
}
}
}
}
//reset all state of enemy
public void ResetState()
{
target = null;
chaseTarget = false;
destinationPosition = ;
ctrlAnimState = ;
}
void SetDefualtColor()
{
int index = 0;
while(index < ){
defaultColor[index] = modelMesh[index].;
index++;
}
}
void GetDamageColorReset()
{
int index = 0;
while(index < ){
modelMesh[index]. = defaultColor[index];
index++;
}
StartCoroutine(GetDamageColor());
}
private IEnumerator GetDamageColor(float time){
//if take damage material monster will change to setting color
int index = 0;
Color[] colorDef = new Color[];
while(index < ){
colorDef[index] = modelMesh[index].;
modelMesh[index]. = colorTakeDamage;
index++;
}
yield return new WaitForSeconds(time);
index = 0;
while(index < ){
modelMesh[index]. = colorDef[index];
index++;
}
yield return 0;
StopCoroutine("GetDamageColor");
}
public void InitTextDamage(Color colorText,string damageGet){
// Init text damage
GameObject loadPref = (GameObject)("TextDamage");
GameObject go = (GameObject)Instantiate(loadPref,
+ (*), );
<TextDamage>().SetDamage(damageGet,
colorText);
}
public void EnemyLockTarget(GameObject targetlock)
{
target = targetlock;
}
public void DeadTransperentSetup()
{
int index = 0;
alphaShader = ("Transparent/Diffuse");
while(index < )
{
modelMesh[index]. = alphaShader;
index++;
}
startFade = true;
deadTransperent = false;
}
void DeadTransperentAlpha(float speedFade){
//Model enemy will fade alpha to zero when dead
int index = 0;
Color[] colorDef = new Color[];
while(index < ){
colorDef[index] = modelMesh[index].;
Color alphaColor = new
Color(modelMesh[index].,modelMesh[index].
,modelMesh[index].,fadeValue);
modelMesh[index]. =
alphaColor;
if(modelMesh[index]. > 0)
{
if(fadeValue > 0)
fadeValue -= * speedFade;
if(fadeValue < 0)
{
fadeValue = 0;
}
}
index++;
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
//Random move again when collision collider
if( == "Collider" && behavior ==
&& !chaseTarget && !target)
{
ctrlAnimState = ;
moveBehavior = ;
}
}
void AddKillCountQuest()
{
for(int i=0;i<;i++)
{
if([i]. ==
&& [i].questCondition ==
&& [i].isStart)
{
[i].killCount += 1;
break;
}
}
}
//Gizmoz Variable
private float movePhaseCurrent;
private float returnPhaseCurrent;
//Draw Gizmoz
[ExecuteInEditMode]
void OnDrawGizmos()
{
if(movePhase > 0 && movePhase != movePhaseCurrent)
{
= new Color(0f,,0f,);
(,movePhase);
}else
if(returnPhase > 0 && returnPhase != returnPhaseCurrent)
{
= new Color(,0,0,);
(,returnPhase);
}
movePhaseCurrent = movePhase;
returnPhaseCurrent = returnPhase;
}
}
保存模块
代码如下:
using UnityEngine;
using ;
public class CharacterData : MonoBehaviour {
public static string characterName;
public static string enableLoadData;
public static PlayerStatus playerStatus;
public static Quest_Data questData;
void Start()
{
//Auto load data when come to scene
enableLoadData = ("Enable Load");
questData =
("QuestData").GetComponent<Quest_Data>();
if(enableLoadData == "True" && )
{
Invoke("LoadData",);
enableLoadData = "False";
}
}
//Save data method
public static void SaveData()
{
GameObject go = ("Player");
//Find player
playerStatus = <PlayerStatus>();
();
();
();
("Enable Load","True");
}
//Load data method
public void LoadData()
{
GameObject go = ("Player");
//Find player
playerStatus = <PlayerStatus>();
();
();
();
();
}
}
音乐系统模块
using UnityEngine;
using ;
using ;
public class SoundManager : MonoBehaviour {
[]
public class SoundGroup{
public string soundName;
public AudioClip audioClip;
}
public List<SoundGroup> sound_List = new List<SoundGroup>();
public static SoundManager instance;
public void Start(){
instance = this;
}
public void PlayingSound(string _soundName){
(sound_List[FindSound(_soundName)].audioClip,
);
}
private int FindSound(string _soundName){
int i = 0;
while( i < ){
if(sound_List[i].soundName == _soundName){
return i;
}
i++;
}
return i;
}
}
测试要点
测试范围
测试范围 主要内容 简要说明
游戏界面功能测试 人物创建功能及跳转功能测试 功能性测试
杀敌功能测试 测试人物与敌人之间的碰撞功能 功能性测试
测试方法
功能性测试:黑盒测试