初试3d游戏的制作

  8人阅读  评论(0)  收藏  举报

目录(?)[+]

题目:3d塔防游戏制作

一.准备阶段

1.学生:
唐凡   董晟

2.平台及使用材质包:
Unity3d  ,Tower Defense Toolkit,c#

       3.分工:
唐凡: 编码及页面布局.
董晟: 材料包查找及使用解释,流程图绘制。

     4.流程图绘制:

https://www.gliffy.com/




二. 具体过程

1.安装unity3d

       首先是安装软件,我们是按照这个网站安装的,说的很详细。http://www.zf3d.com/Products_rj.asp?id=1068  倒是


装的时候出现了缺失材料包的现象,后来去网上找了一堆材质包。倒是解决了这个问题。

2.一些基本的概念及要点

     灯源(light):就像名字一样,这个是给予照明的,没有这个,摄像机就只能看见一片黑。
     摄像机(camera):摄像机的拍摄的部分就是玩家视野内的部分。

     

     运动脚本:采用的是c#制作的运动逻辑控制脚本。

3.目录设计与结构图

这个游戏的结构图如下:

         


4.操作步骤

(一)建立塔基安放点,即采用cube模块,把长宽设置为9,高设置为0.25。一共配置了八块。
cube配置
(二)光源和摄像机安放,大概设置在45角向下投射。

       

·
大概是 这个角度



(三)路径点脚本,采用的c#,采用的是  Tower Defense Toolkit 的模板,我进行了一点修改。使得路径点符合路径
using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public class Path : MonoBehaviour {



public Transform[] waypoints;
public float heightOffsetOnPlatform=1;

public float dynamicWP=1;

public bool generatePathObject=true;
public bool showGizmo=true;

private Transform thisT;
//private GameObject thisObj;

private List<PathSection> path=new List<PathSection>();


void Awake(){
thisT=transform;
//thisObj=gameObject;
}

void Start(){

//run through the list, generate a list of PathSection
//if the element is a platform, generate the node and set it to walkable
for(int i=0; i<waypoints.Length; i++){
Transform wp=waypoints[i];

//check if this is a platform, BuildManager would have add the component and have them layered
if(wp.gameObject.layer==LayerManager.LayerPlatform()){
//Debug.Log("platform");
Platform platform=wp.gameObject.GetComponent<Platform>();
path.Add(new PathSection(platform));
}
else path.Add(new PathSection(wp.position));
}

//scan through the path, setup the platform pathSection if there's any
//first initiate all the basic parameter
for(int i=0; i<path.Count; i++){
PathSection pSec=path[i];
if(path[i].platform!=null){
//get previous and next pathSection
PathSection sec1=path[Mathf.Max(0, i-1)];
PathSection sec2=path[Mathf.Min(path.Count-1, i+1)];

//path[i].platform.SetNeighbouringWP(sec1, sec2);
pSec.platform.SetPathObject(this, pSec, sec1, sec2);

pSec.platform.SetWalkable(true);
if(!pSec.platform.IsNodeGenerated()) 
pSec.platform.GenerateNode(heightOffsetOnPlatform);

pSec.platform.SearchForNewPath(pSec);
}
}

//now the basic have been setup,  setup the path
//~ for(int i=0; i<path.Count; i++){
//~ if(path[i].platform!=null){
//~ path.platform.GetPath();
//~ }
//~ }

if(generatePathObject){
CreateLinePath();
}
}


//create line-renderer along the path as indicator
void CreateLinePath(){

Vector3 offsetPos=new Vector3(0, 0, 0);

for(int i=1; i<waypoints.Length; i++){
//waypoint to waypoint
if(path[i].platform==null && path[i-1].platform==null){
GameObject obj=new GameObject();
obj.name="path"+i.ToString();

Transform objT=obj.transform;
objT.parent=thisT;

LineRenderer line=obj.AddComponent<LineRenderer>();
line.material=(Material)Resources.Load("PathMaterial");
line.SetWidth(0.3f, 0.3f);

line.SetPosition(0, waypoints[i-1].position+offsetPos);
line.SetPosition(1, waypoints[i].position+offsetPos);
}
//platform to waypoint
else if(path[i].platform==null && path[i-1].platform!=null){
GameObject obj=new GameObject();
obj.name="path"+i.ToString();

Transform objT=obj.transform;
objT.parent=thisT;

LineRenderer line=obj.AddComponent<LineRenderer>();
line.material=(Material)Resources.Load("PathMaterial");
line.SetWidth(0.3f, 0.3f);

List<Vector3> path1=path[i-1].GetSectionPath();

line.SetPosition(0, path1[path1.Count-1]+offsetPos);
line.SetPosition(1, waypoints[i].position+offsetPos);
}
//waypoint to platform
else if(path[i].platform!=null && path[i-1].platform==null){
GameObject obj=new GameObject();
obj.name="path"+i.ToString();

Transform objT=obj.transform;
objT.parent=thisT;

LineRenderer line=obj.AddComponent<LineRenderer>();
line.material=(Material)Resources.Load("PathMaterial");
line.SetWidth(0.3f, 0.3f);

List<Vector3> path1=path[i].GetSectionPath();

line.SetPosition(0, waypoints[i-1].position+offsetPos);
line.SetPosition(1, path1[0]+offsetPos);
}
//platform to platform
else if(path[i].platform!=null && path[i-1].platform!=null){
GameObject obj=new GameObject();
obj.name="path"+i.ToString();

Transform objT=obj.transform;
objT.parent=thisT;

LineRenderer line=obj.AddComponent<LineRenderer>();
line.material=(Material)Resources.Load("PathMaterial");
line.SetWidth(0.3f, 0.3f);

List<Vector3> path1=path[i-1].GetSectionPath();
List<Vector3> path2=path[i].GetSectionPath();

line.SetPosition(0, path1[path1.Count-1]+offsetPos);
line.SetPosition(1, path2[0]+offsetPos);
}

}

foreach(PathSection ps in path){
if(ps.platform==null)
Instantiate((GameObject)Resources.Load("wpNode"), ps.pos+offsetPos, Quaternion.identity);
}

}

public List<PathSection> GetPath(){
return path;
}

void OnDrawGizmos(){
if(showGizmo){
Gizmos.color = Color.blue;
if(waypoints!=null && waypoints.Length>0){

for(int i=1; i<waypoints.Length; i++){
Gizmos.DrawLine(waypoints[i-1].position, waypoints[i].position);
}
}
}
}

}


public class PathSection{
public Platform platform;
public Vector3 pos;

private List<Vector3> sectionPath=new List<Vector3>();
private int pathID=0; //a unique randomly generated number assigned whenever a new path is found
//so the unit on this platform can know that a new path has been updated

public PathSection(Vector3 p){
pos=p;
sectionPath.Add(pos);
}

public PathSection(Platform p){
platform=p;
}

public void SetSectionPath(List<Vector3> L, int id){
sectionPath=L;
pathID=id;
}

public List<Vector3> GetSectionPath(){
return sectionPath;
}

public int GetPathID(){
return pathID;
}
}  

(三)UI设计

拖拽下方的按钮到上方的工作区,添加脚本。

按钮列表


            
工作区

(四)敌人生成器

敌人分为两种,一种是地面单位,一种是空中单位,需要写不同的脚本控制生产器的定时装置,以做到交替产生敌人。代码是自动生成的,只需要修改一下速度和血量设置。(另外说一句,血量的显示是生成的两个条,一个不断减小,另一个不变。)

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


public class UnitCreep : Unit {


public float moveSpeed=3;
//修改这里的变量
public bool immuneToSlow=false;

public bool flying=false;
public float flightHeightOffset=3f;

public int[] value=new int[1];

public GameObject spawnEffect;
public GameObject deadEffect;
public GameObject scoreEffect;

public GameObject animationBody;
private Animation aniBody;
public AnimationClip[] animationSpawn;
public AnimationClip[] animationMove;
public AnimationClip[] animationHit;
public AnimationClip[] animationDead;
public AnimationClip[] animationScore;
public float moveAnimationModifier=1.0f;

public AudioClip audioSpawn;
public AudioClip audioHit;
public AudioClip audioDead;
public AudioClip audioScore;

[HideInInspector] public Vector3 dynamicOffset;

[HideInInspector] public int waveID;

public GameObject spawnUponDestroyed;
public int spawnNumber;



public override void Awake () {
base.Awake();

SetSubClassInt(this);

if(thisObj.collider==null){
SphereCollider col=thisObj.AddComponent<SphereCollider>();
col.center=new Vector3(0, 0.0f, 0);
col.radius=0.25f;
}

if(animationBody!=null){
aniBody=animationBody.GetComponent<Animation>();
if(aniBody==null) aniBody=animationBody.AddComponent<Animation>();

if(animationSpawn!=null && animationSpawn.Length>0){
foreach(AnimationClip clip in animationSpawn){
aniBody.AddClip(clip, clip.name);
aniBody.animation[clip.name].layer=1;
aniBody.animation[clip.name].wrapMode=WrapMode.Once;
}
}

if(animationMove!=null && animationMove.Length>0){
foreach(AnimationClip clip in animationMove){
aniBody.AddClip(clip, clip.name);
aniBody.animation[clip.name].layer=0;
aniBody.animation[clip.name].wrapMode=WrapMode.Loop;
}
}

if(animationHit!=null && animationHit.Length>0){
foreach(AnimationClip clip in animationHit){
aniBody.AddClip(clip, clip.name);
aniBody.animation[clip.name].layer=3;
aniBody.animation[clip.name].wrapMode=WrapMode.Once;
}
}

if(animationDead!=null && animationDead.Length>0){
foreach(AnimationClip clip in animationDead){
aniBody.AddClip(clip, clip.name);
aniBody.animation[clip.name].layer=3;
aniBody.animation[clip.name].wrapMode=WrapMode.Once;
}
}

if(animationScore!=null && animationScore.Length>0){
foreach(AnimationClip clip in animationScore){
aniBody.AddClip(clip, clip.name);
aniBody.animation[clip.name].layer=3;
aniBody.animation[clip.name].wrapMode=WrapMode.Once;
}
}
}

if(spawnEffect!=null) ObjectPoolManager.New(spawnEffect, 5, false);
if(deadEffect!=null) ObjectPoolManager.New(deadEffect, 5, false);
}


public override void Start () {
if(!flying) thisObj.layer=LayerManager.LayerCreep();
else thisObj.layer=LayerManager.LayerCreepF();

base.Start();
}

public void SetMoveSpeed(float moveSpd){
moveSpeed=moveSpd;
}


//init a unitCreep, give it a list of Vector3 point as the path it should follow
public void Init(List<Vector3> waypoints, int wID){
base.Init();
waveID=wID;
wp=waypoints;
wpMode=true;

if(flying) thisT.position+=new Vector3(0, flightHeightOffset, 0);

currentMoveSpd=moveSpeed;

if(aniBody!=null && animationMove!=null && animationMove.Length>0){
foreach(AnimationClip clip in animationMove){
aniBody.animation[clip.name].speed=currentMoveSpd*moveAnimationModifier;
}
}

float allowance=BuildManager.GetGridSize();
dynamicOffset=new Vector3(Random.Range(-allowance, allowance), 0, Random.Range(-allowance, allowance));
thisT.position+=dynamicOffset;

PlaySpawn();
PlayMove();
}

//init a unitCreep, give it a path instance so it can retirve the path
public void Init(Path p, int wID){
base.Init();
waveID=wID;
path=p;
wpMode=false;

if(flying) thisT.position+=new Vector3(0, flightHeightOffset, 0);

currentMoveSpd=moveSpeed;

if(aniBody!=null && animationMove!=null && animationMove.Length>0){
foreach(AnimationClip clip in animationMove){
aniBody.animation[clip.name].speed=currentMoveSpd*moveAnimationModifier;
}
}

//if using dynamic wap pos, set an offset based on gridsize
if(path.dynamicWP>0){
//float allowance=BuildManager.GetGridSize()*0.35f;
float allowance=path.dynamicWP;
dynamicOffset=new Vector3(Random.Range(-allowance, allowance), 0, Random.Range(-allowance, allowance));
thisT.position+=dynamicOffset;
}
else dynamicOffset=Vector3.zero;

if(spawnEffect!=null) ObjectPoolManager.Spawn(spawnEffect, thisT.position, Quaternion.identity);
PlaySpawn();
PlayMove();
}

public void Dead(){
GameControl.GainResource(value);
//for(int i=0; i<value.Length; i++){
// GameControl.GainResource(i, value[i]);
//}

if(deadEffect!=null) ObjectPoolManager.Spawn(deadEffect, thisT.position, Quaternion.identity);
float duration=PlayDead();
StartCoroutine(Unspawn(duration));

//spawn more unit if there's one assigned
if(spawnUponDestroyed!=null){

SpawnManager.AddActiveUnit(waveID, spawnNumber);

for(int i=0; i<spawnNumber; i++){
//generate a small offset position within the grid size so not all creep spawned on top of each other and not too far apart
float allowance=BuildManager.GetGridSize()/2;

float x=Random.Range(-allowance, allowance);
float y=Random.Range(-allowance, allowance);

Vector3 pos=thisT.position+new Vector3(x, 0, y);
GameObject obj=ObjectPoolManager.Spawn(spawnUponDestroyed, pos, thisT.rotation);

UnitCreep unit=obj.GetComponent<UnitCreep>();
unit.Init(path, waveID);
//resume the path currently followed by this unit
StartCoroutine(unit.ResumeParentPath(wpMode, wp, wpCounter, currentPS, subPath, currentPathID, subWPCounter));
}
}
}

public void Score(){
if(scoreEffect!=null) ObjectPoolManager.Spawn(scoreEffect, thisT.position, Quaternion.identity);
float duration=PlayScore();

StartCoroutine(Unspawn(duration));
}

public override void Update () {
base.Update();

//~ if(GetWPCounter()==wp.Count){
//~ //final waypoint reached
//~ WaypointReachEvent();
//~ ObjectPoolManager.Unspawn(thisObj);
//~ }
}

public void Stunned(){
if(aniBody!=null){
if(animationMove!=null && animationMove.Length>0){
for(int i=0; i<animationMove.Length; i++){
aniBody.Stop(animationMove[i].name);
}
}
}
}

public void Unstunned(){
PlayMove();
}

private AnimationClip[] animationAttack;
private AnimationClip animationIdle;

public void SetAttackAnimation(AnimationClip[] aniAttack){
animationAttack=aniAttack;

if(aniBody!=null && animationAttack!=null && animationAttack.Length>0){
foreach(AnimationClip clip in animationHit){
aniBody.AddClip(clip, clip.name);
aniBody.animation[clip.name].layer=5;
aniBody.animation[clip.name].wrapMode=WrapMode.Once;
}
}
}

public void SetIdleAnimation(AnimationClip aniIdle){
animationIdle=aniIdle;
if(aniBody!=null){
aniBody.AddClip(animationIdle, animationIdle.name);
aniBody.animation[animationIdle.name].layer=-1;
aniBody.animation[animationIdle.name].wrapMode=WrapMode.Loop;
}
}

public void StopAnimation(){
if(aniBody!=null) aniBody.Stop();

if(aniBody!=null && animationIdle!=null){
aniBody.Play(animationIdle.name);
}
}

public void ResumeAnimation(){
PlayMove();
}

public bool PlayAttack(){
if(aniBody!=null && animationAttack!=null && animationAttack.Length>0){
aniBody.CrossFade(animationAttack[Random.Range(0, animationAttack.Length-1)].name);
return true;
}
return false;
}

public void PlayMove(){
if(aniBody!=null && animationMove!=null && animationMove.Length>0){
aniBody.Play(animationMove[Random.Range(0, animationMove.Length-1)].name);
}
}

public void PlaySpawn(){
if(aniBody!=null && animationSpawn!=null && animationSpawn.Length>0){
aniBody.CrossFade(animationSpawn[Random.Range(0, animationSpawn.Length-1)].name);
}

if(audioSpawn!=null) AudioManager.PlaySound(audioSpawn, thisT.position);
}

public void PlayHit(){
//Debug.Log("Play hit");
if(aniBody!=null && animationHit!=null && animationHit.Length>0){
aniBody.CrossFade(animationHit[Random.Range(0, animationHit.Length-1)].name);
}

if(audioHit!=null) AudioManager.PlaySound(audioHit, thisT.position);
}

public float PlayDead(){
float duration=0;

if(aniBody!=null){
aniBody.Stop();
}

if(aniBody!=null && animationDead!=null && animationDead.Length>0){
int rand=Random.Range(0, animationDead.Length-1);
aniBody.CrossFade(animationDead[rand].name);
duration=animationDead[rand].length;
}

if(audioDead!=null){
AudioManager.PlaySound(audioDead, thisT.position);
duration=Mathf.Max(audioDead.length, duration);
}

return duration;
}

public float PlayScore(){
float duration=0;

if(aniBody!=null && animationScore!=null && animationScore.Length>0){
int rand=Random.Range(0, animationDead.Length-1);
aniBody.CrossFade(animationScore[rand].name);
duration=animationScore[rand].length;
}

if(audioScore!=null) {
AudioManager.PlaySound(audioScore, thisT.position);
duration=Mathf.Max(audioScore.length, duration);
}

return duration;
}

IEnumerator Unspawn(float duration){
yield return new WaitForSeconds(duration);
ObjectPoolManager.Unspawn(thisObj);
}


}


(五)运行图:








三.感想心得


我们在这一次的作业中,应老师的要求对做了一个3D基础的软件游戏。在这次作业完成过程中,我们不仅学习到了对软件开发的技术方面的知识,而且还学习到了一种不满足于已有软件的精神。对于我们学习计算机的人来说 ,本身对于软件的开发与维护就是我们以后工作的重点之一。而对于这种工作来说,一颗创新与不满足的心就是特别重要的。对已有软件性能上缺陷的改进,BUG的修复,以及对界面友好度的提升
而这次的作业当中,我们也初步体会到了身为一个程序员维护的自己程序的辛苦。其中不仅要求在代码开发的还对代码做好备注,还要求我们不能对自己已开发的东西满足,要一直有进取精神,这样才能将自己做的代码做得更加出色


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值