Houdini日记 | 闲得蛋疼之可视化小甲鱼吃鱼小游戏

前天晚上睡前突然想起了当初接触Python时的灵魂导师小甲鱼,然后想到了他的一道经典课后题。

游戏编程:

按以下要求定义一个乌龟类和鱼类并尝试编写游戏。
假设游戏场景为范围(x, y)为0<=x<=10,0<=y<=10
游戏生成1只乌龟和10条鱼
它们的移动方向均随机
乌龟的最大移动能力是2(Ta可以随机选择1还是2移动),鱼儿的最大移动能力是1
当移动到场景边缘,自动向反方向移动
乌龟初始化体力为100(上限)
乌龟每移动一次,体力消耗1
当乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20
鱼暂不计算体力
当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束。

思路分析

首先可以确定的是这种需要过程递推的方式在solver内进行。

那么在solver之前需要解决:

1.随机分布

可以把乌龟和鱼抽象成两组点,用pointWangle在 0-10的范围内撒点。同时要注意坐标取整问题,可以借助floor()函数。(如果有round()可以四舍五入就好了)

Solver内需要解决:

2.随机移动

因为结果需要用关键帧递推来实现,所以随机移动的随机seed也可以用当前帧数也就是@Frame,作为seed。
rand()返回的随机数是[0,1] 的浮点值,借助fit01() 适配到 [-1,1] 或者[-2,2] 同时用floor()取整就可以模拟乌龟和鱼的移动步长了。

3.体力值,移动消耗

可视化的体力值可以用pscale来表现,暂定初始值为1,solver的每一次运算, pscale -= 0.01。

4.乌龟吃鱼

只要判断鱼的点和乌龟的点坐标是否重合就可以了,如果相同:

  1. 乌龟的体力值pscale += 0.2 ,
    2.鱼的点被去除,这里用到removepoint() 。
    如果不相同则继续进行下一轮推导。


Ok, let's dive into Houdini!

首先是在空白的集合体内撒点,这里用到pointWrangle。

float seed = chf('random_seed');
for(int i=0; i<10; i++){
    int fishX = floor((rand(i+seed)*10));
    int fishZ = floor((rand(i+134+seed)*10));
    
    vector fishP = set(fishX,0,fishZ);
    addpoint(0, fishP);
}

注:直接创建wrangle并用addpoint()理论上是不会出现点的,因为所有的wrangle都需要输入端。
再注:同样的seed返回的随机数是相同的,这里用手动添加seed和增加常量的方法,目的是让fishX和fishZ的值有所区别。

需要将Run Over从point 改为Detail once

同样的方法创建乌龟的点组,借助attribute create为乌龟组添加pscale属性,默认更改为1,同时为小鱼组添加一个随机0.2-0.4的pscale,主要是视觉上比较好看。


为两组点定义组名
geometry spreadsheet观察参数变化

从这里可以发现,最后一个永远都是乌龟,前面的是小鱼。那么只要通过npoint()来查找总点数-1 = 乌龟的点序列。


接下来进入solver开始推导,Solver主要解决三个问题,

1.鱼的移动,每次在[-1,1]的整数值间选择;
2.乌龟的移动,每次在[-2,2]的整数值间随机选择,且伴随体力消耗-0.01;
3.乌龟吃鱼,当乌龟点和小鱼点重合时,小鱼点被剔除,且乌龟体力 +0.2;
需要解决一个问题,鱼和乌龟的移动是二维的,如何选定到底是在X轴还是在Z轴移动?这里可以基于当前帧做随机数,然后取整返回0或者1,定义一下0代表移动X,1代表移动Z。
同时要注意,rand(@Frame) 返回的值是在[0,1]区间内的,如果直接floor()取整,获得1的概率非常小,显然不公平。所以这里需要将 [0,1]区间扩大到[0,2],此时再用floor(),返回0和1的概率就相同了。

结构图

首先是鱼的游动问题,选定fish所在分组,分别将X轴和Z轴移动作为属性捆绑到每个点,通过随机数配合不同的seed来完成随机筛选,这里额外定义了一个@first属性,用来控制在X轴或Z轴移动。
然后需要判断是否存在“碰壁”情况。如果在0位置且移动数值是-1,则返回0-(-1) ,如果是在9位置且移动数值为1,则返回9-(1),在0和9之间,就直接简单粗暴 P.x + stepX就好了。

小鱼移动代码如下
i@stepX = floor(fit01(rand(@Frame*(@ptnum+1)+1231235)*2,-1,1));
i@stepZ = floor(fit01(rand(@Frame*(@ptnum+1)+1235)*2,-1,1));
i@first = floor(fit01(rand(@Frame*(@ptnum+1)+12345)*2,0,1));

//first = 0 ---------------> moveX
if(@first == 0){
    i@stepZ =0;
    
    if(@P.x <= 0 && @stepX < 0){
        @P.x -= @stepX;
    }
    if(@P.x >= 9 && @stepX > 0){
        @P.x -= @stepX;
    }  
    if( 0<@P.x<9){
        @P.x += @stepX;
    }
}

//first = 1 ---------------> moveZ
if(@first == 1){
    i@stepX = 0;
    
    if(@P.z <= 0 && @stepZ < 0){
        @P.z -= @stepZ;
    }
    if(@P.z >= 9 && @stepZ > 0){
        @P.z -= @stepZ;
    }  
    if( 0 < @P.z < 9){
        @P.z += @stepZ;
    }
}

同样的,乌龟移动的代码也可以在小鱼的基础上更改,只需要把数值范围扩大到[-2,2]就好了,这里记得更改一下随机seed,以便减少鱼和乌龟移动路径的重复性。同时不要忘记勾选wrangle针对的乌龟组。(此处更加明白了面向对象编程的重要性)

乌龟移动代码如下
i@stepX = floor(fit01(rand(@Frame*(@ptnum+1)+123123565)*2,-1,1));
i@stepZ = floor(fit01(rand(@Frame*(@ptnum+1)+123235)*2,-1,1));
i@first = floor(fit01(rand(@Frame*(@ptnum+1)+12234345)*2,0,1));

//first = 0 ---------------> moveX
if(@first == 0){
    i@stepZ =0;
    
    if(@P.x <= 0 && @stepX < 0){
        @P.x -= @stepX;
    }
    if(@P.x >= 9 && @stepX > 0){
        @P.x -= @stepX;
    }  
    if( 0<@P.x<9){
        @P.x += @stepX;
    }
}
//first = 1 ---------------> moveZ
if(@first == 1){
    i@stepX = 0;
    
    if(@P.z <= 0 && @stepZ < 0){
        @P.z -= @stepZ;
    }
    if(@P.z >= 9 && @stepZ > 0){
        @P.z -= @stepZ;
    }  
    if( 0 < @P.z < 9){
        @P.z += @stepZ;
    }
}
//pscale ---------------> -0.01
f@pscale -= 0.01;
注:不要忘记乌龟是有体力值的!每次移动体力值-0.01。


接下来就是乌龟吃鱼的部分了!

乌龟如果没吃到鱼自然不需要有什么运算,直接继续解算继续走就可以了。
如果乌龟吃到了鱼,首先乌龟体力值+0.2,然后小鱼被从几何体中剔除。
因为是在乌龟和鱼都存在的几何体层面工作,分组筛选已经行不通了,但是!之前我们说过乌龟肯定是最后一个点,所以求得的总点数-1 = 乌龟的点序号。借助npoints()可以返回当前几何体的点数。
乌龟吃鱼需要用到for循环,循环的次数为鱼的个数,也就是总点数-1 。每次移动后用乌龟的坐标和每一条鱼的坐标去对比,如果相同则鱼被吃掉同时乌龟体力值+0.2。
读取乌龟和小鱼的点坐标,可以借助point()。


point
借助setpointattrib()来修改乌龟的pscale。
setpointattrib
借助removepoint()来剔除被吃掉的点。
removepoint
注:这两个函数内的point_num都可以用循环中的指针i来引导。


结构如下

bite.png

代码如下

int npt = npoints(0);

vector turtleP = point(0,'P', npt-1);
for( int i=0; i<npt-1; i++){
    if( point(0, 'P', i) == turtleP){
    float npscale = point(0, 'pscale', npt-1) + 0.2;
    setpointattrib(0, 'pscale', npt-1, npscale);
    removepoint(0, i);
    }
}

至此主要程序架构就结束了,通过copy to point 选择不同的组来进行几何体复制,这里用了Houdini自带的乌贼和海龟玩具。


copytopoint选项注意勾选pack and instance

注:

勾选pack and instance是为了让几何体以内存指针形式存在,从而不需要复制详细信息转而以一个点来取代,也就是之前我们写入的小鱼和乌龟的点。
同时要检查geometry spreadsheet中点的坐标是否正确,如果和输入点的数值有差别,通常是因为没有将Pivot Location 改为Origin。

最终完成的乌龟吃鱼动图


如果深入还可以从以下几点入手:
1.几何体的移动方向伴随转动;
2.几何体移动从跳动变为线性移动;

有时间再回来迭代吧!先睡了!

Cheers !

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341