练手的第一个游戏

《计算机游戏开发

课题实验报告

名:李基铭

级:2014211602

学号:2014212123

指导教师:李学明

北京邮电大学数字媒体与设计艺术学院

一、游戏简介

《梦游大逃杀》是一款基于UNITY3D开发制作的第三人称视角射击生存类游戏,其游戏背景是一个画风非常卡通的小朋友,在梦游过程中拿着假想的枪支和自己的毛绒玩具们展开了逃跑或是追杀(依关卡难度而定),游戏本身的每一个关卡均没有固定的结束机制(例如到达安全屋或者杀掉足够数量的敌人)而是更加类似于FPS游戏中的生存模式,玩家可以根据关卡的设定制定不同的策略与战术,并在逐渐增多的敌人面前存活尽可能长的时间。

二、游戏规则

由于所学知识的限制和素材原因,我并不能在短时间内制作出丰富的剧情,但是游戏本身拥有四个关卡和开始界面五个场景素材,且主角人物和敌人均设定了动作动画。游戏内所有模型(人物,地形,环境)均有碰撞检测,并为地方单位受到攻击时添加了粒子效果。游戏本身拥有循环的背景音乐,且开枪,受到伤害,死亡均设计了独立音效。考虑到内存和模型的原因,在枪支的设定上并未采用子弹设定,而是从枪管方向添加了一条很长的ShootRay,在主色调偏暗的游戏场景内也更容易瞄准。

游戏开始界面如下:

其中Stage Select部分由四个关卡代表的按钮组成,点击每个按钮就可以进入该关卡,如果点击Start按钮则直接进入第一个关卡。此外还在右下角设置了音量按钮,可以在游戏开始前调节游戏内部的音量(0-100)

开始游戏后以第三关举例:

游戏视角跟随持枪的主角,屏幕正上方的是分数,其中三种敌人:

僵尸兔子分值20;僵尸小熊分值30,僵尸大象分值80,击杀难度/造成伤害也随着分值相应不同,在击杀的时候处理优先级也根据游戏关卡不同而不同。

游戏左下角是生命值,根据每个关卡的难度/机制而不同,例如第三关的敌人伤害较高,且人物射速较低,因此生命值为300(相比于第一关的100和第三关的500略有不同,但是数值具体仅经过了初步平衡,游戏具体的难度体验因人而异);在人物受到攻击时屏幕会出现红色,提醒玩家注意规避:

在生命数值归零后,游戏会结束,播放玩家死亡音效,敌人停止追逐玩家并且玩家不能继续进行射击和移动,在1秒后进入游戏结束画面:

玩家再次点击屏幕后,回到游戏的开始界面进行关卡选择。

游戏的关卡设计了四关,分别叫Late Night(夜半);Nightmare(噩梦);Dawn(黎明);Polar(极圈);四关的游戏机制如下:

夜半是游戏的初始关卡,难度较低,设定上人物移动速度较快,且枪支的射速较快(对应的伤害较低,但总体DPS比较平衡,适合不熟悉控制的人熟悉瞄准机制),三种敌人刷新速度也较慢,两种血量较低的敌人(兔子和熊)刷新时间为3秒,大象刷新时间为5秒。此外,对应30每发/每秒10发的射速,击杀小型敌人所需时间很短,因此主要难度是利用地形和走位规避伤害并尽可能多的击杀大象。

噩梦和夜半类似,是难度较高的夜半,例如敌人的伤害变高(通过提高攻击间隔,单次伤害实现),血量变高,但控制人物的血量没变,但相应提高了枪的伤害(例如击杀大象原来需要17发,现在需要14发),相应的存活时间也会降低,因此难度较高,但同时人物的射速与单发伤害与第一关并无区别,玩家只需要一直按住左键然后向敌人反方向跑就可以了。

黎明的设计理念与前两关不同,如果说前两关的武器是轻型自动步枪,这关的武器则是狙击枪。单发伤害提升到170意味着可以单发击杀100血量的兔子,200血量的熊需要两枪,500血量的大象需要三枪。相应的,人物的血量也提高为300,同时射速被削减到了0.5发每秒。此外,在该关卡中玩家的移动速度被提高到了8.0f(前两关为6.0f),可以拉开距离进行远距离的精确瞄准,由于设定时为地形中的障碍设计了碰撞检测(设计navigation时烘培了所有模型),敌人无法穿过的模型子弹也无法穿过,因此关卡难度也很大。

极圈的设计类似于上世纪的游戏抢滩登陆等的理念,玩家的移动速度被极大限制但是射速和伤害被极大提高,在该关卡中人物移动速度仅为1.0f,但射速,单发伤害极高,对应的敌人刷新速度也很快且个人血量也很高,因此该关卡下玩家只需要站在原地然后击退一波又一波的敌人即可。

三.设计理念:

1.UI设计思路:

游戏本身由两类场景组成:开始界面和游戏关卡,其中开始界面的设计难点在于UI的布置,不同场景之间的连接和控件功能的实现;游戏关卡的设计难点在于人物动作之间的衔接,主角和敌方人物的移动控制和武器的命中判定。

游戏的UI为了契合游戏本身的卡通风格,因此大部分使用了较为活泼的字体和颜色设计,例如游戏从标题,关卡选择到游戏结束使用的均是这种叫LUCKIEST-GUY的字体,但同时作为一个打僵尸的游戏,背景音乐设定上采用了比较阴森但是较为清澈的打击乐。

在界面设计中,由于游戏本身色调偏暗,因此背景使用了紫色,但标题采用了鲜艳的颜色(手调的黄色和浅蓝色,不一定好看)

之后为Dream-Walking使用Animation功能添加了动画效果,让开始界面显得不那么突兀。此外,为了增加标题的层次感,为两段text分别添加了阴影,经过调整后蓝色字体添加了黑色阴影,黄色字体添加了深黄色阴影,看起来比较像3D制作的标题。

同理,在关卡选择部分的UI设计也为了整体的一致性采用了同样的字体:

图标本身使用了UI中的button选项,并为每一个按钮的image组件添加了关卡的截图。在设计时考虑到unity的button都是圆角按钮,为了整体的美观性想把关卡按钮也做成圆角的,但是采用图片作为按钮背景时无论图片是否是圆角图片,保存成jpg格式后导入到texture中的结果都是矩形图片(圆角图片导入后会留下四个白色的角);查阅资料发现需要使用shader组件进行代码控制,但在这版本的游戏中还没有实现。

UI部分代码实现思路:

游戏中用到的UI设计主要包含以下三部分:关卡切换,玩家信息显示,音量调节;分别用一段代码进行实现。

I. 关卡切换部分:

利用Load Level/Load Scene函数均可实现,为了避免bug加入了Debug.Log函数;完成代码后在界面中的Button组件中使用OnBtnClick方法调用,并将每个关卡和代码关联即可。

关卡切换部分代码如下:

publicclassGameStartManager:MonoBehaviour{

publicButtonStartButton;

publicvoidOpenScene00()

{

Debug.Log("Game Started");

Application.LoadLevel("Level 00");

}

publicvoidOpenScene01()

{

Debug.Log("Game Started");

Application.LoadLevel("Level 01");

}

publicvoidOpenScene02()

{

Debug.Log("Game Started");

Application.LoadLevel("Level 02");

}

publicvoidOpenScene03()

{

Debug.Log("Game Started");

Application.LoadLevel("Level 03");

}

publicvoidOpenScene04()

{

Debug.Log("Game Started");

Application.LoadLevel("Level 04");

}

}

II. 关卡中对人物的信息显示:

屏幕中主要需要显示人物的分数和血量,和游戏结束后的Game Over界面;

分数实现非常简单,只需要构建一个Text文件然后设置每个敌方单位的得分并相加即可。

分数的代码实现:

publicstaticintscore;

Texttext;

voidAwake()

{

text = GetComponent ();

score = 0;

}

voidUpdate()

{

text.text ="Score: "+ score;

}

}

III.人物血量的显示:

血量的代码实现稍后人物生命值部分会讲解,只需要将人物血量显示到已经在代码中声明过的Health Slider中即可,此处粘贴一下该部分代码:

publicvoidTakeDamage (intamount)

{

damaged =true;

currentHealth -= amount;

healthSlider.value = currentHealth;

playerAudio.Play ();

if(currentHealth <= 0 && !isDead)

{

Death ();

}

}

IV. 游戏通关界面:

人物死亡后的Game Over动画的实现也非常简单,只需要在Animation中构建三部分:蓝色背景的透明-----不透明,分数的高亮(大小变大再变小)和Game Over字样的显示。

在Animation中构建完成后只需要在代码中调用该Animation组件即可。

代码如下:

Animatoranim;

voidAwake()

{

anim = GetComponent();

}

V. 音量调节部分:

需要首先创建一个Slider,之后向Slider中添加Audio Source组件,并将背景音乐和该组件关联。

之后向该Slider中添加代码即可。该部分代码实现如下:

usingSystem.Collections;

usingSystem.Collections.Generic;

usingUnityEngine;

usingUnityEngine.UI;

publicclassMusicAdministor:MonoBehaviour

{

publicSliderVolume;

publicvoidOnVolume()

{

GetComponent().volume = Volume.value;

}

}

2. 对第一人称人物的设计:

I.控制人物的设计思路:

从代码实现角度讲分三部分实现:人物的移动;人物的伤害判定;人物的射击判定。

从人物模型的角度讲同样分三部分实现:人物模型/动作的关联(Animator组件),人物模型移动的判定(对地表模型的烘培,navigation模块),人物开枪的动作设计

II.控制人物移动的设计:

人物的移动核心代码如下:

floath =Input.GetAxisRaw("Horizontal");

floatv =Input.GetAxisRaw("Vertical");

voidMove(floath,floatv)

{

movement.Set(h, 0f, v);

movement = movement.normalized * speed *Time.deltaTime;

playerRigidbody.MovePosition(transform.position + movement);

}

首先利用input.getaxisraw方法获得来自键盘/鼠标的控制系数,Horizontal和Vertical是获得键盘上下/左右键按下时触发的,后面调用人物旋转时使用的Mouse X,Mouse Y同理,获得的是鼠标沿着屏幕的横/纵轴进行移动时获得的长度。

之后设置人物的移动面为X和Z(可以在人物模型的刚体组件内进行如下修改:

设置人物可以在XZ轴内进行平移)

之后对人物的移动设置一个三维数组movement,将Y值设置为0,并在代码开始定义人物的移动速度:public float speed = 6.0f ,便于之后各关卡对人物的移动速度进行调节。

之后设置人物的位置为原位置+速度x时间,并每帧调用一次该函数即可。

III.人物的方向控制设计:

人物的旋转函数与之类似:

voidTurning()

{

RaycamRay =Camera.main.ScreenPointToRay(Input.mousePosition);

RaycastHitfloorHit;

if(Physics.Raycast (camRay,outfloorHit, camRayLength, floorMask))

{

Vector3playerToMouse = floorHit.point - transform.position;

playerToMouse.y = 0f;

QuaternionnewRotation =Quaternion.LookRotation(playerToMouse);

playerRigidbody.MoveRotation(newRotation);

}

}

获取人物鼠标位置的方法如上,创建三维数组后定义PlayerToMouse初始位置为玩家位置面向鼠标位置,旋转角度的改变量为地面位置-改变位置,同样每帧调用一次该方法。

IV.人物的动画衔接设计:

之后对人物的动画衔接进行设置:首先从Animator窗口对人物动作进行绑定:

提取到的动画模组有三个:Move(移动),Idle(发呆),Death(死亡)。

之后对Conditions中添加两个变量作为三种模组之间的转换:

控制Idle-Move/Move-Idle的判定情况:Is Walking;是Bool型变量,Is True情况下转为Move动画,Is False情况下转为Idle动画。

控制Any State-Death的判定情况:Is Dead;是Trigger型变量,触发时结束所有当前动作并进入Death动画。

完成调节后对时间轴的切换点进行设置,因为进入Idle状态需要一点时间的动画延续以防止人物动作过于僵硬,因此将Move动画稍微延长一段,并勾选Has Exit Time选项。而另外一边的开始移动如果使用Has Exit Time就会在人物开始移动时位置出现平移而没有动画效果,所以需要取消掉Has Exit Time选项。

voidAnimating(floath,floatv)

{

boolwalking = h != 0f || v != 0f;

anim.SetBool("IsWalking", walking);

}

利用该代码判定人物是否在移动(通过对水平/垂直平面的坐标进行判定),并修改Is Walking模组的数据。

voidDeath ()

{

isDead =true;

playerShooting.DisableEffects ();

anim.SetTrigger ("Die");

playerAudio.clip = deathClip;

playerAudio.Play ();

playerMovement.enabled =false;

playerShooting.enabled =false;

}

利用该代码判定人物是否存活(通过人物的血量判断是否存活,若死亡则停止人物的射击和移动动画),并修改Is Alive模组的数据。

V.人物的射击判定:

因为人物的射击通过ShootRay思路进行实现,所以对开火的设计如下:

1.设置单发伤害

2.设置开火间隔

3.设置射击有效距离

4.将开火时的粒子效果/光效/音效在代码中进行定义,并在Unity中进行绑定

5.利用代码设置:左键按下判定开火;若距离上一发开火时间>开火间隔则设计,否则不射击

6.定义射击方向:从枪口开始,做一条长度为开火距离,角度为鼠标指向/人物角度的射线

7.定义射击伤害判定:若射线和模型collider发生碰撞,且目标具有生命值(考虑到可能会射击到地形/墙面/地形上)则造成伤害

控制射击部分的代码如下:

publicintdamagePerShot = 20;

publicfloattimeBetweenBullets = 0.3f;

publicfloatrange = 100f;

floattimer;

RayshootRay =newRay();

RaycastHitshootHit;

intshootableMask;

ParticleSystemgunParticles;

LineRenderergunLine;

AudioSourcegunAudio;

LightgunLight;

floateffectsDisplayTime = 0.2f;

voidAwake()

{

shootableMask =LayerMask.GetMask ("Shootable");

gunParticles = GetComponent ();

gunLine = GetComponent ();

gunAudio = GetComponent();

gunLight = GetComponent ();

}

voidUpdate()

{

timer +=Time.deltaTime;

if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets &&Time.timeScale!=0

{

Shoot ();

}

if(timer >= timeBetweenBullets * effectsDisplayTime)

{

DisableEffects ();

}

}

voidShoot ()

{

timer = 0f;

gunAudio.Play();

gunLight.enabled =true;

gunParticles.Stop ();

gunParticles.Play ();

gunLine.enabled =true;

gunLine.SetPosition (0, transform.position);

shootRay.origin = transform.position;

shootRay.direction = transform.forward;

if(Physics.Raycast (shootRay,outshootHit, range, shootableMask))

{

EnemyHealthenemyHealth = shootHit.collider.GetComponent ();

if(enemyHealth !=null)

{

enemyHealth.TakeDamage (damagePerShot, shootHit.point);

}

gunLine.SetPosition (1, shootHit.point);

}

else

{

gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);

}

}

}

VI.人物的生命值判定:

控制人物的生命判定如下:

1.设定人物的生命上限,

2.将人物生命值投射到UI中的slider中,

3.通过生命值对Is Alive(上文中提到的判定模组)进行判断,以对人物的动画进行设置。

4.设置敌人的伤害参数(此处只是定义变量,后面设置敌人的攻击代码时会填入参数)

5.为了伤害效果逼真,在人物受到伤害时让屏幕变红,实现方法为在游戏关卡的UI中添加一个和屏幕一样大的红色image,平常透明度为0,若受到伤害则讲透明度重置为100%,0.5秒后回复。该方法中利用color.lerp函数对图片的颜色进行设置。

6.设置人物如果生命值降低至0时,触发Death动画,并在0.5秒内播放Game Over界面(和受伤的红色界面类似,需要在外界通过animation和UI进行设计,但是在游戏中只需要调用触发函数)

7.Game Over后调用Load Scene函数,回到主界面选择重新开始。

代码实现如下:

usingUnityEngine;

usingUnityEngine.UI;

usingSystem.Collections;

usingUnityEngine.SceneManagement;

publicclassPlayerHealth:MonoBehaviour

{

publicintstartingHealth = 100;

publicintcurrentHealth;

publicSliderhealthSlider;

publicImagedamageImage;

publicAudioClipdeathClip;

publicfloatflashSpeed = 5f;

publicColorflashColour =newColor(1f, 0f, 0f, 0.1f);

Animatoranim;

AudioSourceplayerAudio;

PlayerMovementplayerMovement;

PlayerShootingplayerShooting;

boolisDead;

booldamaged;

voidAwake()

{

anim = GetComponent ();

playerAudio = GetComponent ();

playerMovement = GetComponent ();

playerShooting = GetComponentInChildren ();

currentHealth = startingHealth;

}

voidUpdate()

{

if(damaged)

{

damageImage.color = flashColour;

}

else

{

damageImage.color =Color.Lerp (damageImage.color,Color.clear, flashSpeed *Time.deltaTime);

}

damaged =false;

}

publicvoidTakeDamage (intamount)

{

damaged =true;

currentHealth -= amount;

healthSlider.value = currentHealth;

playerAudio.Play ();

if(currentHealth <= 0 && !isDead)

{

Death ();

}

}

voidDeath ()

{

isDead =true;

playerShooting.DisableEffects ();

anim.SetTrigger ("Die");

playerAudio.clip = deathClip;

playerAudio.Play ();

playerMovement.enabled =false;

playerShooting.enabled =false;

}

publicvoidRestartLevel ()

{

SceneManager.LoadScene(0);

}

}

关卡结束部分代码:

voidUpdate()

{

if(playerHealth.currentHealth <= 0)

{

anim.SetTrigger("GameOver");

restartTimer +=Time.deltaTime;

if(restartTimer >= restartDelay)

{

Application.LoadLevel(Application.loadedLevel);

}

}

}

3.敌方单位设计思路:

和控制人物类似,对敌方单位的设计也从以下方面着手:敌人血量,敌人移动,敌人伤害,和敌人的动画构建,和人物不同的是由于动画素材限制,敌人没有远程攻击(只有近战,对应的也利用Animator进行不同动画的关联),并且敌人需要设置Navigation组件以智能的寻找玩家位置。

I.对敌人移动方式的设计:

1.设置敌人的基本移动速度;

2.将敌人的位置命名为一个三维数组;并定义敌人的新位置为原位置+速度x Delta.time,每帧执行一次。

3.为敌人设计碰撞检测;以便后面对敌人的攻击玩家/地形碰撞进行处理;

4.判定玩家的血量和敌人本身的血量,若血量>0则利用nav函数表达敌人的方向为面向玩家位置。

敌人移动的代码实现如下:

usingUnityEngine;

usingSystem.Collections;

usingUnityEngine.AI;

publicclassEnemyMovement:MonoBehaviour

{

publicfloatenemySpeed = 100.0f;

Transformplayer;

PlayerHealthplayerHealth;

EnemyHealthenemyHealth;

NavMeshAgentnav;

Vector3enemyMovement;

RigidbodyenemyRigidbody;

voidAwake()

{

player =GameObject.FindGameObjectWithTag("Player").transform;

playerHealth = player.GetComponent();

enemyHealth = GetComponent();

nav = GetComponent();

enemyRigidbody = GetComponent();

}

voidMove(floath,floatv)

{

enemyMovement.Set(h, 0f, v);

enemyMovement = enemyMovement.normalized * enemySpeed *Time.deltaTime;

enemyRigidbody.MovePosition(transform.position + enemyMovement);

}

voidUpdate()

{

if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)

{

nav.SetDestination(player.position);

}

else

{

nav.enabled =false;

}

}

}

II. 对敌人的血量设计:

1.设置敌人的初始血量(因敌人的不同而异),并设计击杀后尸体消失的时间和增加的分值

2.定义敌人遭到攻击时的粒子特效,声效。

3.设置尸体消失的动画:由于Animation中可以看出敌人死后是平躺的,所以不需要加入旋转函数,只需要在Z轴上对敌人尸体进行平移即可,Z坐标为原坐标+尸体消失速度x Delta Time,每帧调用一次。

4.设置敌人遭到攻击(在人物开枪的代码中定义过伤害判定,此处只需要定义血量削减函数并通过get component调用受伤时的粒子特效和音效即可。

敌人血量的代码设计如下:

usingUnityEngine;

usingUnityEngine.Events;

publicclassEnemyHealth:MonoBehaviour

{

publicintstartingHealth = 100;

publicintcurrentHealth;

publicfloatsinkSpeed = 2.5f;

publicintscoreValue = 10;

publicAudioClipdeathClip;

Animatoranim;

AudioSourceenemyAudio;

ParticleSystemhitParticles;

CapsuleCollidercapsuleCollider;

boolisDead;

boolisSinking;

voidAwake()

{

anim = GetComponent ();

enemyAudio = GetComponent ();

hitParticles = GetComponentInChildren ();

capsuleCollider = GetComponent ();

currentHealth = startingHealth;

}

voidUpdate()

{

if(isSinking)

{

transform.Translate (-Vector3.up * sinkSpeed *Time.deltaTime);

}

}

publicvoidTakeDamage (intamount,Vector3hitPoint)

{

if(isDead)

return;

enemyAudio.Play ();

currentHealth -= amount;

hitParticles.transform.position = hitPoint;

hitParticles.Play();

if(currentHealth <= 0)

{

Death ();

}

}

publicvoidStartSinking ()

{

GetComponent ().enabled =false;

GetComponent ().isKinematic =true;

isSinking =true;

ScoreManager.score += scoreValue;

Destroy (gameObject, 2f);

}

}

此外,和人物死亡动画类似,敌人死亡也需要调用Death----Idle的Animator模组,此处也是定义一个Trigger变量Death,和人物死亡一样----取消Exit Time并在代码中调用该段动画。

敌人的animator如下:

三.对敌人的攻击设计

敌人对玩家的攻击动作和人物射击敌人的设计方式类似,步骤如下:

1.设置敌人的攻击间隔和攻击伤害(作为全局public变量,便于拖入敌人的prefab后修改数值,以改变敌人的难度)

2.调用玩家人物,作为敌人的移动方向和攻击目标(此处同样调用人物的血量以判定人物是否存活)

3.进行碰撞检测(此处的碰撞检测和敌人与地形碰撞检测并不一样,此处是判定敌人的攻击区域内是否存在玩家----若存在则运行攻击玩家的方法)

4.判定玩家人物的血量,若血量在0+且进入了敌人的攻击范围则进行一次攻击判定(并开始计算敌人的攻击动作的冷却时间)

攻击设计的代码实现如下:

usingUnityEngine;

usingSystem.Collections;

publicclassEnemyAttack:MonoBehaviour

{

publicfloattimeBetweenAttack = 0.5f;

publicintattackDamage = 10;

Animatoranim;

GameObjectplayer;

PlayerHealthplayerHealth;

EnemyHealthenemyHealth;

boolplayerInRange;

floattimer;

voidAwake()

{

player =GameObject.FindGameObjectWithTag ("Player");

playerHealth = player.GetComponent ();

enemyHealth = GetComponent();

anim = GetComponent ();

}

voidOnTriggerEnter(Colliderother)

{

if(other.gameObject == player)

{

playerInRange =true;

}

}

voidOnTriggerExit(Colliderother)

{

if(other.gameObject == player)

{

playerInRange =false;

}

}

voidUpdate()

{

timer +=Time.deltaTime;

if(timer >= timeBetweenAttack && playerInRange && enemyHealth.currentHealth > 0)

{

Attack ();

}

if(playerHealth.currentHealth <= 0)

{

anim.SetTrigger ("PlayerDead");

}

}

voidAttack ()

{

timer = 0f;

if(playerHealth.currentHealth > 0)

{

playerHealth.TakeDamage (attackDamage);

}

}

}

四.设计步骤:

由于游戏本身内容复杂,为了较为形象的解释该游戏从空场景到可以构建为成熟场景的过程,在此处简要的解释一下该游戏设计过程中的主要步骤:

1.构建一个游戏场景,并导入下载好的环境prefab文件(此处包含灯光设计)

2.导入玩家的人物模型,并在Animator中导入人物的不同动画,并创建变量来规范动作间的变换触发点和持续时间

3.对人物模型进行优化,首先对人物的枪支进行设计,加入开火的特效,并添加脚本对开火行为进行构建

枪支的Inspector界面最后如下图:

4.对人物的移动进行设计,其中包括镜头的跟随,人物的移动旋转和移动动画的触发,并在代码中对各个功能进行实现

人物本身的Animator最后如下:

人物本身的Inspector最后如下:

5.完成对玩家控制人物的调试后,对敌人的角色进行设计;首先将敌人的模型导入到游戏中;

6.对敌人的Animator进行控制,将各种动画通过添加变量的方式进行关联,并在代码中对各个变量的作用时间分别进行定义。

敌人的Animator窗口如下:

7.对敌人的移动方式进行控制,通过代码实现敌人的移动和攻击,并设定敌人对地形的碰撞体积和对玩家进行攻击的碰撞体积。

设计完成后敌人的Inspector窗口如下:

8.完成敌人移动的设计后需要对场景进行Navigation的烘培,对地形的角度/人物模型进行对比后将台阶等设置进行平衡,最后点击烘培

烘培后的地形效果如下:

蓝色部分为可移动平面。

9.将单个敌人保存为Prefab,并将同样的脚本内容赋值给三种不同的模型(对动画效果重新进行绑定),并修改人物的属性(移动速度,单次伤害,伤害间隔,血量等)

10.设置怪物刷新的位置,并使用相应的代码进行实现

11.对游戏场景进行相应的细化,例如灯光,镜头远近,背景音乐设置等

12.完成游戏场景的布置后对场景进行build,并以此类推进行以下几个游戏关卡的设计

13.完成游戏关卡的设计后开始进行UI界面的设计,对每关人物的血量/分数等利用代码进行调用,并完成Game Over动画的实现。

14.对游戏开始界面的GUI进行设计,包括关卡切换/音量调节等,并利用相关的代码进行关卡切换等功能的实现。

15.对所有场景进行build操作,并对游戏进行基本测试,完成后导出游戏素材。

五.后记

本游戏参考了Unity官方教程https://unity3d.com/cn/learn/tutorials/projects/survival-shooter-tutorial

该教程从

12个方面对游戏设计进行了讲解,是我在unity官网完成的第五部游戏开发教程,整个视频课程约300分钟,我从该视频课程中受益匪浅,学到了相当多Unity中各方面的开发知识和前端代码的实现与程序构建。

视频的播放列表链接如下:

https://unity3d.com/cn/learn/tutorials/projects/survival-shooter/camera-setup?playlist=17144

本游戏非本人制作的部分如下:

游戏关卡的地形场景;游戏中玩家和敌人的模型;模型使用的动画;游戏UI中使用的字体。

参考了教程中的部分如下:

游戏中玩家和敌人的运动代码参考了部分视频中给出的案例;

游戏内灯光的布置和设计;

游戏内Navigation地形的渲染设置。

独立设计部分:

游戏机制,人物开火动画,关卡,开始界面UI,分数判定等。

游戏素材内包含以下部分,如果需要检查素材可以按以下方式检查:

代码部分:

分为玩家,敌人,摄像机,游戏机制四部分:

游戏内场景部分:

包括Level 00(开始界面)和Level 01-04(游戏关卡)

游戏内环境材质:

(来自素材包)

游戏内Prefab:

包括环境,枪和受伤的粒子特效,光效和玩家/敌人的模型。

游戏内材质球:

(来自素材包)

游戏内字体:

游戏内声音素材:

游戏内人物受伤/死亡音效;背景音乐等

(来自素材包)

游戏内动画效果素材:

主界面UI的动画效果,游戏结束效果和设计时枪口的动画效果

六.组员信息:

游戏设计人:

14级数媒技术2班 李基铭 

负责工作:

模型设计,动画设计,关卡设计,人物设计,配乐,游戏机制设计,代码设计,动画组件设计,等等等等

游戏设计时间:大约30小时(包括写报告时间5小时,海报制作2小时,看视频10小时和独立制作10小时)

推荐阅读更多精彩内容