Map Generator
背景
技术
开发工具
unity
Version 2018.2.21f1
涉及到的动画技术
1. Perlin噪声
用于生成随机数,Perlin噪声算法表现出了一定的自然性,因为它能生成符合自然排序(平滑)的伪随机数序列。
2. 细胞自动机
一种离散模型,它是由无限个有规律、坚硬的方格组成,每格均处于一种有限状态。整个格网可以是任何有限维的。同时也是离散的。每格于t时的态由t-1时的一集有限格(这集叫那格的邻域)的态决定。每一格的“邻居”都是已被固定的。(一格可以是自己的邻居。)每次演进时,每格均遵从同一规矩一齐演进。
例子:生命游戏
3. 粒子系统
粒子系统是一系列独立对象的集合,可用于模拟各种自然现象。通常粒子系统在三维空间中的位置与运动是由发射器控制的。发射器主要由一组粒子行为参数以及在三维空间中的位置所表示。粒子行为参数可以包括粒子生成速度(即单位时间粒子生成的数目)、粒子初始速度向量(例如什么时候向什么方向运动)、粒子寿命(经过多长时间粒子湮灭)、粒子颜色、在粒子生命周期中的变化以及其它参数等等。
涉及到的交互技术
unity3D物理引擎中的刚体组件
灵感来源
这个项目作为我们交互媒体的期末大作业,老师的要求是需要实现三个动画技术,而且需要有交互。在观察《代码本色》中实现的技术之后,我将自己的关注点放在“自然”、“随机”、“生命规律”这几点上。那么我们生活在的真实世界怎么能用代码最大化的贴合上它们的规律呢?想一想你小时候有没有一个疑问“我是怎么来到世界上的?世界到底是什么?”说的直白一点,我们是物质的,我们的世界也是物质的,我们生活在宇宙中的一颗叫地球的行星,我们遵循着某种规律在发展在进化,我们的社会随着这些生产力的发展来发展经济,连带着宗教、文化等规范,一步步发展到今天。我们似乎习惯了世界带给我们的应接不暇的惊喜,我们不在为它怎么形成而感到疑惑…
那如果,让你当造物主呢,你在一片空白上造出草地、海洋、再进一步你可以在草地上种上树木…听起来很简单吧,就想画画,只要落笔就可以了,但能用代码做到的远不止这些。
造物主不会刻意修饰、设计。事物都是随机产生、随机变化、任其自由发展的,每一次创造都不会相同,就像海面的每一次波动都是独一无二的,而这些,我们用代码就可以实现。
Map Generator就是一款简单的随机地图生成器,让你通过它重新思考世界的样子,也通过加入弓箭这种交互增加了趣味性,下面展示一下它的实现吧。
展示
功能实现
使用柏林噪声生成地图
每次运行得到的地图都是随机的,里面的元素是不变的,但元素组合发生了变化。
定义一个二维数组储存地图,然后定义一个随机位置最为种子,确保每次地形都不同。随即位置不能为整数,不然会出现统一输出0.4652731。由于噪声图是通过灰度图的值来随机的,所以输出后得到一个0-1的小数。可以用它来放大10倍(可以取整),得到随即地形的高度。然后保存到二维地图数组中。也可以根据地形的高度设置地面的种类。(草地或沙漠)。
使用细胞自动机生成树木
从上文的二维数组获取地形的种类(设定树木只能生成在草地上)。先定义一个十字形的种子,种子的地图类型为树木。然后遍历地图,如果周围九宫格内的树木数量超过2棵,而且这个格子的类型不是树木,则在这里种下一棵树。
使用柏林噪声、粒子系统模拟水面
首先定义一个存储粒子的数组和粒子发射器。同理,使用柏林噪声得到每一个粒子起伏的高度。可以通过高度设置颜色。在粒子发射器中勾选粒子随机角度旋转。
使用向量模拟物理
敌人发射抛物线的弓箭。在这里使用了unity的刚体组件。首先是玩家的坐标减去敌人的坐标,得到弓箭飞行的方向。由于弓箭收到重力影响,需要一个向上的力组合成形成抛物线的力。需要求出弓箭的飞行时间t。由F=ma,L=at可以得出t=Lm/F。由于是抛物线,时间t还要除以2。所以向上的力为:向量上m(t/2)*G。然后与玩家方向的力相加。当然可以加一些干扰,让弓箭有误差。
交互展示
发射弓箭
与设定的敌军交战
代码实现
地图生成
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NoiseMapMaker : MonoBehaviour
{
public GameObject GlassCube;
public GameObject SandCube;
public GameObject[] trees;
private GroundType[,] grounds;
private Vector3[,] groundsPos;
[SerializeField] private float density = 15;
private Vector2 seed;
[SerializeField] private float maxHeight = 10;
[SerializeField] private int width = 50, height = 50;
[SerializeField] private float mapSize = 1;
//生成地图
void SetY(int w, int h)
{
float x = (w + seed.x) / density;
float y = 0;
float z = (h + seed.y) / density;
y = Mathf.PerlinNoise(x, z) * maxHeight;
y &#