Unity或Unity3D最佳实践和技巧

Share

此资源包含由我们的网络成员提供的Unity或Unity3D最佳实践和技巧的集合.

此资源包含由我们的网络成员提供的Unity或Unity3D最佳实践和技巧的集合, 并将定期更新其他信息和新兴的Unity技术. 这是一个社区驱动的项目, 因此,我们也鼓励您做出贡献, 我们期待着你们的反馈.

Unity是一个跨平台游戏引擎, 在这里,我们将拥抱Unity和Unity3D的最佳实践,这将使你成为一个更好的游戏开发者.

Check out the Toptal resource pages 有关Unity或Unity3D的更多信息 interview questions.

针对单个对象的有效光线碰撞检测

光线在Unity中对于实现碰撞和UI交互非常有用. 官方文档提供了使用它们的典型方法:

    RaycastHit hit;
    float distanceToGround = 0;

    if (Physics.Raycast(transform.position, -Vector3.up, out hit, 100.0F)) {
	    //确定哪个对象正在使用hit.对撞机,对它做点什么
    }

检测所有击中物体的等效方法是:

    RaycastHit[] hits;
    hits = Physics.RaycastAll(transform.position, transform.forward, 100.0F);

However, 如果我们只是想看看一个特定的物体是否挡住了光线呢, 而不是一一列举, 我们不确定(或者不在乎)它是否会大受欢迎? 我们可以通过从它的边界开始而不是从射线开始来检查单个物体, using the Bounds.IntersectsRay method:

        Ray ray = ... // our ray
	GameObject go = ... // our game object
	Collider collider = go.GetComponent();
	if (collider.bounds.IntersectsRay(ray)) {
		// object hit!
	}

这也可以与 Bounds object provided by a MeshRenderer.

从Toptal获取最新的开发人员更新.

订阅意味着同意我们的 privacy policy

如何使用“深度优先搜索”算法访问层次结构中的所有元素?

Sometimes, 开发人员需要找到或测试由复杂转换关系组成的复杂结构中的元素. 要找到或测试所需的元素,必须访问 all 上述结构的节点.

通常,转换被组织为一个复合体 tree data structure,访问所有树节点的最常用算法之一是 Depth-first search. 该算法从左到右递归地访问最内层节点优先级的所有节点.

using System;

//使用DepthFirstSearch算法访问所有节点,每次访问时调用' p_callback '.
public bool TraverseDFS(Transform p_root,Predicate p_callback)
{

  //‘Predicate’是一个c#委托,它接受一个参数并返回一个bool值
  //我们可以使用这个' bool '来检查用户是否想继续搜索树.
  if(!p_callback(p_root))
  {
    //找到所需的查询,我们可以停止搜索.
    return false;
  }

  for(int i=0;i

Contributors

Eduardo Dias da Costa

自由Unity或Unity3D开发人员
Brazil

Eduardo是一名拥有超过十年客户端和前端应用开发经验的开发者. 他总是乐于学习和接受新的挑战,这可以使他掌握新的语言和/或技术. 他专攻计算机图形学, image processing, game development, tools development (CLI, desktop, etc.),以及UI/UX/前端开发.

Show More

如何从场景中正确地摧毁物品?

在游戏的某个地方,你的玩家消灭了一个怪物或挑选了一个道具. 现在,您的代码必须从场景中删除这些实例.

新开发人员通常会弄错 gameObject’s components, such as the Transform and the attached MonoBehaviours 作为场景中的主要实例.

//Reference to the scripts
MonsterScript monster;
ItemScript item;

void OnPlayerWin()
{
    //Process score.
    Destroy(monster); //Will destroy the monster’s script only and the monster will be on scene. 
}

void OnPlayerGetItem()
{
    //Process item.
    Destroy(item); //Will destroy the item’s script only and the item will be on scene. 
}

Unity API中的每个组件都有一个对其 gameObject,它是包含的元素 all 与游戏元素相关的脚本和组件.

//Reference to the scripts
MonsterScript monster;
ItemScript item;

void OnPlayerWin()
{
    //Process score.
    Destroy(monster.gameObject); //Will destroy the monster’s entire instance.
}

void OnPlayerGetItem()
{
    //Process item.
    Destroy(item.gameObject); //Will destroy the item’s entire instance.
}

区分a的知识 gameObject 它的组成部分对于避免游戏玩法关键部分的不良行为至关重要.

然而,有时,目标实际上是终止给定的脚本,为另一个脚本打开一个位置. AI行为之间的变化就是一个例子.

GameObject monster;

void OnPlayerClose()
{
    AIScriptIdle ai = monster.GetComponent(); //Gets the current AI instance
    if(ai) Destroy(ai); //If it exists, destroy.
    monster.AddComponent(); //Adds the Attack AI Script.
}

void OnPlayerFar()
{
    aisscriptattack ai =怪物.GetComponent(); //Gets the current AI instance
    if(ai) Destroy(ai);//如果存在,则销毁.
    monster.AddComponent(); //Adds the Idle AI script.
}

Contributors

Eduardo Dias da Costa

自由Unity或Unity3D开发人员
Brazil

Eduardo是一名拥有超过十年客户端和前端应用开发经验的开发者. 他总是乐于学习和接受新的挑战,这可以使他掌握新的语言和/或技术. 他专攻计算机图形学, image processing, game development, tools development (CLI, desktop, etc.),以及UI/UX/前端开发.

Show More

如何在运行时为游戏对象定制材质?

Sometimes you have one material that is configured to render your character with the correct shader and parameters; but your game could have a great number of characters with different textures and parameters for each.

通常,一个人会为每个人创建一个材料. However, 如果在某一点基础材料需要它的着色器, 纹理或参数改变, 您需要更新所有以前创建的.

避免这种情况的一种方法是为所有角色使用一种材质,并将参数和纹理存储在角色脚本中.

//Character.cs

Texture2D skin; //Reference to the character skin texture.
Color tint;     //Some tint parameter for the shader.

void Start()
{
    Material m = GetComponent().sharedMaterial; //Get the renderer material reference.
    m.color = tint;       //Change the shader color parameter to the character’s.
    m.mainTexture = skin; //Change the skin texture to the character’s.
}

Pretty easy? However, there is a catch. 为了简化工作流程,我们只有一种材质 all characters. 所以,如果有人改变了材质属性,所有的角色都会受到影响.

To avoid this, 你必须在游戏开始时复制材质实例,并使其专属于该角色.

//Character.cs

Texture2D skin; //Reference to the character skin texture.
Color tint;     //Some tint parameter for the shader.

void Start()
{
    Material m = GetComponent().sharedMaterial; //Get the renderer material reference.
    m = Instantiate(m);                         //Duplicate the original
    m.color = tint;       //Change the shader color parameter to the character’s.
    m.mainTexture = skin; //Change the skin texture to the character’s.
    GetComponent().sharedMaterial = m; //Assign the new material only for this character.
}

Contributors

Eduardo Dias da Costa

自由Unity或Unity3D开发人员
Brazil

Eduardo是一名拥有超过十年客户端和前端应用开发经验的开发者. 他总是乐于学习和接受新的挑战,这可以使他掌握新的语言和/或技术. 他专攻计算机图形学, image processing, game development, tools development (CLI, desktop, etc.),以及UI/UX/前端开发.

Show More

如何在规定的时间框架内以恒定和/或可变速率将对象移动到所需位置?

Things in games must move. 这只是一个速度、加速度和时间的问题.

将物体移出Unity物理循环的最常见方法是使用 MoveTowards and Lerp.

如果你想以恒定的速度移动物体, MoveTowards 每帧以恒定速率增加你的位置.

MoveTowards

//Constant Speed
Vector3 position;
Vector3 target;
float speed;
void Update()
{
  position = Vector3.致力于(位置、目标、时间.deltaTime * speed);
}

要使物体有加速的感觉,必须使用 Lerp. 我们得到的效果是因为下一个位置是剩余距离的一个百分比. So, 第一步比最后一步大,因为剩下的距离越来越短.

Lerp

//Variable Speed
Vector3 position;
Vector3 target;
float speed;
void Update()
{
  position = Vector3.Lerp(position,target,Time.deltaTime * speed);
}

有趣的是这些方程都是用数字表示的, 考虑到四元数(旋转), colors, 矩形和其他数学结构具有相同的组成, 可以看到,一切都可以使用这种技术进行插值. 例如,褪色或滑动屏幕和旋转对象是它的其他用例.

Contributors

Eduardo Dias da Costa

自由Unity或Unity3D开发人员
Brazil

Eduardo是一名拥有超过十年客户端和前端应用开发经验的开发者. 他总是乐于学习和接受新的挑战,这可以使他掌握新的语言和/或技术. 他专攻计算机图形学, image processing, game development, tools development (CLI, desktop, etc.),以及UI/UX/前端开发.

Show More

使用材质池避免过多实例化

In another tip here, Eduardo Dias da Costa向我们展示了如何为不同的游戏对象定制材料. 这是一个很好的实践,可以节省很多创建材料的工作. However, 在对象将频繁生成和稍后销毁的情况下, 这种技术可能导致泄漏, 因为Unity每次修改并分配给Mesh Renderer时都会生成一个新的材质实例. In this case, 如果您将重复使用具有相同参数的材料, 使用一个材料库是很有用的. 我通常从这个泛型类开始,然后根据需要进行定制:

    /// 
    /// Generic material pool
    /// 
    /// A class or struct that contains the parameters for identifying and constructing a new material
    public class MaterialPool {

        //函数定义给定一个T将返回一个新材料.
        //当材料第一次插入到池中时使用.
        public delegate Material MaterialGenerator(T);

        private Dictionary pool;
        private MaterialGenerator生成器;

        公共MaterialPool(MaterialGenerator生成器){
            this.pool = new Dictionary();
            this.generator = generator;
        }

        public Material GetMaterial(T) {
            Material mat;
            if (!pool.TryGetValue(t, out mat)) {
                mat = generator(t);
                pool[t] = mat;
            }
            return mat;
        }
    }

It requires two additional, customized, 元素:一个材料生成器函数, 以及提供定制材料参数的类型:

///重写Equals和GetHashCode方法时要小心,这样才能正常工作
///作为字典中的键
struct MaterialDef {
...
}

///从定义中生成一个新的材质
private Material MaterialGenerator(MaterialDef matDef) {
...
}

///在Start或Awake实例中设置材质池
    matPool = new MaterialPool(MaterialGenerator);
///稍后在代码中,当需要一个新材料时,只需从池中请求它
gameObject.GetComponent().material = matPool.GetMaterial(新MaterialDef ( ... ));

这种方法的一大好处是,它使您能够分离不同的关注点:

  1. The pooling.
  2. 鉴别一种物质与另一种物质的区别.
  3. 物质生成本身.

您可以替换其中的每一个,而不必担心(太多)其他的. 这种方法的另一个优点是,它允许您从池中查询材料,而不必关心它是否会生成新的材料或重用现有的材料.

Submit a tip

提交的问题和答案将被审查和编辑, 并可能会或可能不会选择张贴, 由Toptal全权决定, LLC.

* All fields are required

Toptal Connects the Top 3% 世界各地的自由职业人才.

Join the Toptal community.