Done is better than perfect

0%

这篇文章覆盖了制作六边形地图所涉及到相关的方法,通用公式和算法。为了文章更加优雅简洁,文中的实例都将使用伪代码进行描述。

几何性质

六边形是由六个边组成的。正六边形的每条边长度相同。我们这里假设所有的六边形都是正六边形。六边形的方向有两个: 1. 垂直列方向(六边形的任意两条平行边在垂直方向上) 垂直方向

  1. 水平行反向(六边形的任意两条平行边在水平方向上) 水平方向

六边形有6个边和6个角。每个边被两个六边形共享。每个角被三个六边形共享。更多关于中心点(centers), 边(sides), 和 角(corners)可以参见这篇文章(四边形,六边性和三角形)

阅读全文 »

文件结构

Lua文件结构

提供外部使用的头文件

  • lua.h 定义了Lua的核心API。
  • lauxlib.h 定义了Lua的辅助API,此文件中定义的函数,是对lua.h中函数的简单封装,方便使用。
  • lualib.h 定义了Lua的标准库(coroutine, table, io, os, string, utf8, math, debug, package)。
  • luaconf.h 定义编译相关的宏,主要用于区分别不同平台的特性以及一些其他编译特性。

核心代码

Lua的核心代码主要分为以下几个模块:

核心API

  • lapi.h 定义了几个栈的操作宏(api_incr_top:栈顶加一, adjustresults:调节栈顶以容纳更多的返回值, api_checknelems:检查栈中是否有对应的栈空间)。
  • lapi.c lua.h头文件中定义核心API的实现。
  • lctype.h C语言的标准字符检查函数头文件,主要检查单个字符的类型,增加了EOZ,允许传入-1(EOZ)进行检测。
  • lctype.c 定义了符号表。
  • ldo.c 处理Lua的栈和调用结构。
  • lfunc.c 定义了用于操纵函数原型和闭包的辅助函数。
  • llimits.c 定义了一些基础的类型和安装依赖信息。
  • lmen.c 内存管理接口。
  • lobject.h Lua中的对象类型定义。
  • lobject.c 对象上的一些操作。
  • lstate.h 定义了lua_State相关的结构,这是Lua的核心结构。
  • lstate.c lstate.h中定义结构的相关操作。
  • lstring.c Lua字符串表,处理和缓存Lua中使用的字符串。
  • ltable.c 定义了Lua中Tale相关的操作。
  • ltm.c 元表相关的定义和操作。
  • ldump.c 将Lua函数转储为预编译块。
  • lundump.c 加载预编译的Lua块。
  • lzio.c 定义了一个buffer流,用于读取数据。

GC管理

  • lgc.c 垃圾回收器

词法,语法和语义分析器

  • llex.c 词法分析器。
  • lparser.h 语法和语义分析器。

Lua虚拟机(解释器)

  • lopcodes.h 定义了Lua虚拟机的操作码(OP_MOVE, OP_LOADI等)跟汇编的MOV指令类型,虚拟机就是用来执行这些指令的。
  • lopnames.h 定义了操作码对应的名字。
  • ljumptab.h 定义了Lua解释器跳转表相关的宏(vmdispatch, vmcase, vmbreak)。
  • lvm.c Lua的虚拟机用于执行opcode。

调试

  • ldebug.c 获取调试信息(函数调用, 行号信息等)。
阅读全文 »

什么渲染管线

渲染管线跟工厂的流水线一样,工厂的流水线为了生成最终的一个产品,每个流水线的工人只干一件特定的事情,最后将一个产品完成。计算机的图形渲染管线和工厂的流水线一样,只是图形渲染管线最终生成的是一张二维图片。 我们将一个渲染流程分成3个阶段:应用阶段几何阶段光栅化阶段。注意这仅仅是概念性阶段,每个阶段本身通常也是一个流水线系统,即包含了子流水线阶段。如下图: 渲染流水线三个概念图

  • 应用阶段

从名字我们可以看出,这个阶段是由我们的应用主导的,因此通常由CPU负责实现。换句话说,我们这些开发者具有这个阶段的绝对控制权。 在这阶段中,开发者有3个主要任务: 1. 我们要准备好场景数据,例如摄像机的位置、视椎体、场景中包含了哪些模型、使用了哪些光源等等; 2. 为了渲染性能,我们往往需要做一个粗粒度剔除(culling)工作,以把哪些不可见的物体踢出去,这样就不需要再移交给几何阶段进行处理; 3. 最后我们要设置好每个模型的渲染状态,这些渲染状态包括但不限于它们使用的材质(漫反射颜色、高光反色颜色)、使用的纹理、使用的Shader等。 这一阶段最重要的输出是渲染所需的几何信息,即渲染图元(rendering primitives)。通俗来讲,渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段。

  • 几何阶段

几何阶段用于处理所有和我们要绘制的几何相关的事情。例如,决定需要绘制的图元是什么,怎么绘制它们,在哪里绘制它们。这一阶段通常在GPU上进行。 几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这个阶段可以进一步分成更小的流水阶段,这在下一章中会讲到。几何阶段的一个重要任务就是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值,着色相等相关信息**,并传递给下一个阶段。

  • 光栅化阶段

这一阶段将会使用上个阶段传递的数据来生成屏幕上的像素,并渲染出最终的图像。这个阶段也是在GPU上运行。光栅化的任务主要是决定每个渲染图元中的哪些像素应该被绘制在屏幕上。它需要对上一个阶段得到的逐顶点数据(例如纹理坐标,顶点颜色等)进行插值,然后在进行逐像素处理。和上一个阶段相似,光栅化阶段也可以分成更小的流水线阶段。

阅读全文 »

前言

Mathjax是一种支持在所有浏览器上显示数学公式的Javascript引擎,它支持绝大部分的Tex/LaTex语法,并且有三种输出HTML/CSS, SVG(矢量图)和MathML(Firefox支持的数公式标记语言)。本文主要介绍如何在将Mathjax整合进入Hexo中,在整合之前先介绍一下Tex/LaTex。

阅读全文 »

Canvas的渲染

渲染基本层次

基本渲染层次是根据可视化对象在继承窗口中的显示顺序来渲染的,在此有两种方式可以调节对象的顺序。第一种:直接在继承窗口拖拽。第二种:通过Transform的SetAsFirstSibling, SetAsLastSibling, and SetSiblingIndex函数来进行调节。

渲染模式

  • 屏幕覆盖模式

直接屏幕映射方式显示,永远在屏幕的最上面,跟相机无关即使没有相机也可以显示

  • 屏幕相机模式

此模式和屏幕模式相似但是受到相机的限制,但是始终都显示在相机的前面,如果有3D物体在UI的前面也是会挡住UI。

  • 世界模式

把Canvas当成普通的对象在世界坐标系中定位渲染。

阅读全文 »

基础回顾

在正式分析UGUI源码前,让我们回顾一下,模型的组成,以及渲染。

模型组成

模型是由一系列的三角形组成,三角形又是由三个顶点构成的,每个顶点上保存了相关的顶点属性(顶点位置,顶点颜色,顶点UV,顶点法线,顶点切线)在Unity中使用Mesh类来表示。

模型渲染

模型筛选:

  1. 首先根据相机的Culling Mask剔除那些不需要此相机渲染的模型。
  2. 然后根据摄像机的视锥体大小,剔除完全在视锥体外的模型。

模型分类:

Unity将根据需要渲染的模型Shader中RenderQueue对模型进行分类,分为不透明模型(小于2500)和透明模型(大于等于2500)。

排序渲染模型:

  1. 首先通过sortLayer和sortingOrder对不透明模型进行进行排序,越低的越先渲染;然后再通过Shader中的RenderQueue进行排序;再然后就通过包围盒中心点的深度(距离摄像机的位置)进行排序;最后如果深度相同则还可以通过ZBias进行深度调节。
  2. 透明物体同样的使用上述规则进行排序。
阅读全文 »

本文主要基于Blender创建一个简单的机器人,并基于此机器人来了解骨骼动画的制作流程。制作骨骼动画主要包含以下步骤:

  1. 建模
  2. 构建骨骼
  3. 蒙皮和权重绘制
  4. 动画制作
阅读全文 »

Unity实现序列帧动画主要有两种方式,一个是通过Unity自带的Animation进行制作,二是根据序列帧动画原理通过代码按固定帧率切换显示的图片即可实现序列帧动画效果。

阅读全文 »

为什么要使用协程

为什么要使用协程?,先来看段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System.Collections;
using UnityEngine;

public class TestCoroutine : MonoBehaviour
{
void Start()
{
StartCoroutine(CoroutineFunc());
}

IEnumerator CoroutineFunc()
{
var wfeof = new WaitForEndOfFrame()
//1000次循环大概1s
for(int i = 0; i < 1000; i++)
{
//假设做大量的工作,大概CPU耗时:0.1s
DoManyWork();
yield return wfeof;
}
}

void DoManyWork()
{
Debug.Log("DoManyWork is called.");
}
}

上面的CoroutineFunc函数里面消耗了大量的CPU时间,在没有协程的开发环境中,比如像C/C++这样的开发语言,一般会创建一个线程来执行这种耗时的操作,多线程开发也会牵扯到各种数据和资源的互斥访问和线程同步的问题,这也导致在其他线程访问Unity引擎相关的组件会出现问题,因为我们没法控制其他的线程在访问操作一个对象时Unity引擎不去访问它,所以在Unity中其他线程一旦访问Unity引擎相关的组件就可能回出错。因此为让Unity的主线程不被这种耗时的操作给卡住,就只有将这种耗时的操作分到多帧里去执行,所以就出现了这种伪线程即协程。

阅读全文 »

委托

什么是委托?委托与事件有什么关系呢?先来看一下委托的定义,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace DotnetExample
{
//定义一个委托
public delegate void VoidCallback();

// Console必须要一个Main函数才能编译通过
class Program
{
static void Main(string[] args)
{
}
}
}
阅读全文 »