Unity笔记 《Unity游戏设计与实现》

这书不错,学习以下设计思路很好。

第零章
介绍了一些Unity基础。
camera控制:alt、ctrl + alt、F按键 的使用。
一个小例子不断polish的过程。
物理控制的经验:
1)避免漂浮感,调整物体大小(rigidbody 1.0 = 1米)或在PhysicsManager中提高Gravity的Y的绝对值(例子中为-20)。
2)固定高度的跳跃

float   y_speed = Mathf.Sqrt(2.0f*Mathf.Abs(Physics.gravity.y)*this.JumpHeight);

v = sqrt( 2 g h )

第一章
卷动背景,移动攻击。
记下可以做成游戏的构思,说不定什么时候会用到。

无限循环背景,处理跟随物体移动过快问题。
根据摄像机位置找到n(整体背景的位置),直接用乘法来计算位置,无论多快的移动用这个算法3块背景就足够了!
这里讨论了两种四折五入的做法,对于负数结果是不同的。
Mathf.RoundToInt( vf );
Mathf.FloorToInt( vf +0.5f );

关于碰撞形状的选择。球和方形挤在一起时会发生位移,而方形和方形挤在一起时却确难的多发生位移。
球碰撞在侧视图中往往会有误差,认为能打到敌人的却没有打到。

加入高风险高回报的机制提高有趣性。这里用的是攻击距离,实际实现用的是发起攻击到伤害的时间。

“预判断”(碰撞体存在比动作稍长)机制让游戏更容易上手。

制作“攻击反馈”。先搞清player面向x正,y是上,z向里对应Vector3.right Vector3.up Vector3.forward。
按照一个轴旋转的Quaternion:

blowout_xz = Quaternion.AngleAxis(y_angle, Vector3.up)*blowout_xz;

=============================================================
Find References In Scene可以找到一个脚本在哪个gameobject上。
这个常见,do{ … }while(false)是一种goto,可以在里面写break。
2个scene,titlescene和gamescene。注意自己打开的是哪个。

AnimatedTextureExtendedUV利用texture的scale和offset实现帧动画,用于刀光的实现。

AttackColliderControl刀的碰撞球。
OnTriggerEnter和OnTriggerStay的区别。

CameraControl和玩家一起移动。
this.transform.position = new Vector3(player.transform.position.x + this.offset.x, this.transform.position.y, this.transform.position.z);

FadeControl动态加入的component算法,使用一个1x1纹理用于全屏淡出。
fader = gameObject.AddComponent();

FloorControl作者得意的无限背景。

GUIControl加入SceneControl处理gui部分,这个ui的弄法较老。

LevelControl生成怪物,这样的方式来初始化。这个类并不是继承monobehaviour所以不会做为组件使用。
this.level_control = new LevelControl();
this.level_control.scene_control = this;
this.level_control.player = this.player;
this.level_control.OniGroupPrefab = this.OniGroupPrefab;
this.level_control.create();

OniControl单个怪物控制。

OniEmitterControl最后结算时的怪物塔,就是简单的绑定了sphere的一些oni随机位置落下。

OniGroupControl怪物方阵控制。

OniStillBodyControl最后结算时的怪物塔。

PlayerControl控制玩家

ResultControl用于结算数据,这个在SceneControl里直接new出使用。
this.result_control = new ResultControl();

SceneControl场景控制模块,放在场景里的empty gameobject上。可以说是主模块。
除了主要的prefab(玩家、oni),还有一个游戏状态的维护(STEP)。

ScoreControl分数显示

SimpleSpriteGUI分数显示用到显示文字

TitleSceneControl,ScoreControl的简化版本,Application.LoadLevel(“GameScene”);装载场景。

第二章
拼图。
点击,2d到3d,碎块的任意位置拖动,要考虑到点击时的偏移量就可以做到了。
移动不移动到鼠标位置,这是个手感问题,作者应该是推崇考虑偏移量。

码放打乱的碎片,建立一个网格放入元素再进行一些变换。

“可控随机数”,这里使用一组周期性变换的数值(指旋转90)效果也不错。

介绍gameobject和component。
render、collider、audio等都是GetComponent的缩写。

以下3中一个意思:
this.rigibody 直接从别的组件访问另外组件。
this.gameObject.rigibody 回到gameObject在访问另外组件。
this.gameObject.GetComponent() 也不使用缩写,原生态的写法。

销毁:
Destory(this); 销毁脚本自己
Destory(this.gameObject) 销毁整个gameObject

[RequireComponent(typeof(MeshCollider))]
public class PieceControl:MonoBehaviour{
这样就会一并添加MeshCollider

PazzleControl通过代码添加组件和MeshCollider

逐渐变慢的滑动插值动画,也可以考虑使用动画插件:

case STEP.SNAPPING:
{
    // 朝目标位置移动
    Vector3 next_position, distance, move;

    // 按Easing方式运动
    distance = this.snap_target - this.transform.position;

    // 下一处位置=当前位置和目标位置的中间点
    distance *= 0.25f*(60.0f*Time.deltaTime);

    next_position = this.transform.position + distance;

    move = next_position - this.transform.position;

    float   snap_speed_min = PieceControl.SNAP_SPEED_MIN*Time.deltaTime;
    float   snap_speed_max = PieceControl.SNAP_SPEED_MAX*Time.deltaTime;

    if(move.magnitude < snap_speed_min) {

        // 移动量小于一定值则结束
        // 快速向目标位置移动
        // 结束判断在状态迁移检测过程中进行,这里只调整其位置
        // 
        this.transform.position = this.snap_target;

    } else {

        // 如果移动速度过快则进行调整
        if(move.magnitude > snap_speed_max) {

            move *= snap_speed_max/move.magnitude;

            next_position = this.transform.position + move;
        }

        this.transform.position = next_position;
    }
}
break;

第三章
吃豆类游戏规则介绍,整数处理grid形式,可以提前按下方向到T形路口改变方向。
所谓的“操作辅助”,改善操作困难的情况。

文本编辑地形。使用CSV(逗号分割的文本),很好的string处理的例子。

自定义了一个editor按钮来生成。

using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(Map))]
public class MapModelCreator : Editor {
    public override void OnInspectorGUI() {
        DrawDefaultInspector ();
        if (GUILayout.Button("Create Map Model")) {
            Map map = target as Map;
            map.CreateModel();
        }
    }
}

动画2技巧:
1)动画混合mix,握剑和挥剑下半身混合行走。

// 开始合成动画的结点
        Transform mixTransform = transform.Find("root/hip/mune");

        // 挥动宝剑
        animation["up_sword_action"].layer = 1;
        animation["up_sword_action"].AddMixingTransform(mixTransform);      

        // 把剑举到胸前
        animation["up_sword"].layer = 1;
        animation["up_sword"].AddMixingTransform(mixTransform);         

2)声音事件,用脚本添加事件的例子。

protected virtual void InitializeAnimations()
    {
        animation["run"].speed = 2.0f;

        // 脚步声事件
        AnimationEvent ev = new AnimationEvent();
        ev.time = 0.0f;
        ev.functionName = "PlayStepSound";
        ev.floatParameter = 1.0f;
        animation["run"].clip.AddEvent(ev);

        AnimationEvent ev2 = new AnimationEvent();
        ev2.time = animation["run"].clip.length / 2.0f;
        ev2.functionName = "PlayStepSound";
        ev2.floatParameter = 1.06f;
        animation["run"].clip.AddEvent(ev2);        
    }
// 播放脚步声
    public void PlayStepSound(AnimationEvent ev)
    {
        (FindObjectOfType(typeof(AudioChannels)) as AudioChannels).PlayOneShot(m_stepSE,1.0f,0.0f,ev.floatParameter);

    }

幽灵AI:
设计4种不同性格的幽灵。

第四章
潜水艇。
3D声音,设置距离衰减。设置Volume rolloff 为custrom rolloff

强行中断声音的播放会有时会出现噪音,所以采用fadeout的方法,书中例子使用了coroutine。

private IEnumerator Fadeout(float duration)
{
     // 淡出
     float currentTime = 0.0f;
     float waitTime = 0.02f;
     float firstVol = audio.volume;
     while (duration > currentTime)
     {
         audio.volume = Mathf.Lerp(firstVol, 0.0f, currentTime / duration);
         yield return new WaitForSeconds(waitTime);
         currentTime += waitTime;
     }

     // 淡出处理完全结束后销毁对象
     if (hitEffector)
     {
         while (hitEffector.IsPlaying())
         {
             yield return new WaitForSeconds(waitTime);
         }
     }
     // 发送销毁对象消息
     transform.parent.gameObject.SendMessage("OnDestroyLicense");
 }

潜艇的控制:
attenuate-衰减。
Mathf.SmoothStep有speed up和slow down的插值。
margin-幅度。

使用不同的摄像机绘制雷达和主画面,通过culling mask来控制外观。通过视口设置来制作画中画(注意这里并没有用到render target)。

============
这个项目管理方式和前面几个不同。
打开root scene

SceneSelector
这个脚本附着在Root上,设置了DontDestroyOnLoad(gameObject);所以是全局存在的。
有2个子(也是全局存在的):
Intermission,过场插页,实际切换发生在IntermissionEffector的Finish里。
RootCamera,clear flag depth only,是个叠加摄像机。

之后用协程加载了title这个场景。
包括的gameobject
Adapter,stage也有一个

 // 场景结束后调用
 void OnSceneEnd()
 {
     // 开始Stage
     if (root) root.SendMessage("OnStartStage");
 }

Field
里面有terrain和submarine,用于title背景。
UI
unity 5之前的ui做法。

GameObject.BroadcastMessage(调用gameobject自身和子的函数)
一个游戏中使用通常是一组对象的集合,用这个可以分别处理。

可以这样用状态机的形式管理整体游戏
root scene:
gameobject gamephasemanager singleton.
setgamephase

gamephase(One scene has N phase) array in manager.
phase 0 phase0begin phase0work phase0end
phase 1
phase 2 …

第五章
节拍游戏。

每个打点过了,就属于下个打点了。
对应打点到屏幕像素。
点击准确程度分成3个等级。这里作者提出了重复判断的情况,不要提前判断后面的节奏。
事件系统,根据音乐产生各种事件。

建立turn around环境。
阀值,某些数字的范围。这里用一个可以调整的误差来控制游戏的难易。
定位音乐的位置,MusicManager

AudioSource m_audioSource;
public void Seek(float beatCount){
        m_audioSource.time =  beatCount / m_currentSongInfo.beatPerSecond;
        m_beatCountFromStart = m_previousBeatCountFromStart = beatCount;
    }

===============================
这个项目只有一个scene。

PhaseManager对应的gameobject在场景里,管理游戏状态。

第六章
飞机游戏。
生成用于锁定的碰撞区域,用扇形区域来避免帧间跳过,而不用效率低的continuous检查方式。

meshFilter.mesh = mesh;

// mesh 发生变化后如果不执行 false -> true 则不会得到反映
meshCollider.enabled = false;
meshCollider.enabled = true;

不连续锁定,这里使用了实例的唯一id。
int targetId = collider.gameObject.GetInstanceID();

制导,轨迹trailrender
圆弧轨迹,每次移动都会增加偏移角度,避免围绕敌人一直无法攻击的情况。也用在AI等地方。

消息窗口:
逐字显示效果。
=========================
打开opening场景
maincamera上有个clicktogamestart会装载game场景

第七章
消除游戏。
递归检测相邻方块,这里需要添加一个已经处理过的标志避免重复处理。为求万全,作者加了递归最大调用次数。
初始的方块码放,作者下了一定的功夫处理这个,根据需求生成了,只有1个4,多个3的随机初始方块。
为搞清除同时上下滑动和替换的动画情况,有个子动画的测试项目。
本来是数组安排的方块,如何处理方块的移动。数组的索引是立即变化的,同时设计偏移距离值,偏移值会插值到0产生动画。
=========================
GlobalParam 提供了singleton的方式
titlecontrol loadlevel gamescene
debugprint singleton的写屏调试方式
用trigger反弹:

Vector3 v = this.rigidbody.velocity;
v.z *= -0.5f;
this.rigidbody.velocity = v;

第八章
猫窜。
试用状态机来管理animation
可以控制的跳跃高度,注意处理2此和下落过程的处理。
碰撞的处理,记录所用的碰撞,然后处理。窗户框的碰撞模型和模型外观不同,有利于模型滑动。
碰撞后的回弹,直接操作rigidbody速度。
基于物理+特殊处理的方式。

第九章
RPG。
Action,使用2维数组记录各种action。
Actor基类来实现各种event。
Event执行,其中有个wait input的处理等待玩家输入。
Message处理特殊情况,通用的再加入到event。

第十章
赛车。
3d空间中的拾取问题。
按照间隔生成路径。
基于路径生成多边形。
扭曲隧道模型。
道路两边的点缀的树木,开始稀少中间茂密最后稀少。

你可能感兴趣的:(unity)