融入动画技术的交互应用——解压小游戏“蒲公英与星”

主题:点线几何与力学所构造的梦幻感

  • 课程:交互媒体专题设计
  • 参考教材:《代码本色》
  • 系统涉及章节内容:
  1. 第一章:向量速度&加速度
  2. 第二章:引力&斥力
  3. 第三章:弹力
  4. 第四章:粒子系统
  5. 第五章:碰撞事件

一、背景

这个小游戏是《交互媒体专题设计》这门课的作业,这门课算是上学期《互动媒体技术》的延伸,这学期老师主要围绕《代码本色》这本书,讲解数学与力学在编程中的应用。当力学系统与数学公式应用到代码中,一些很奇妙的效果会出现,往往会呈现出一种带有科技感和未来感的效果,比如大家最熟悉的分形几何。

做这个小游戏的初衷是想给自己解解压(作业实在太多,不如直接做个能解压的东西),预期在游戏里能够呈现出比较让人放松的画面,游戏玩法也会比较简单有趣(太刺激或者难度太大的游戏都有违解压的初衷)。

二、创意

定好方向之后我就开始考虑,什么东西能让我觉着放松。我首先想到的是……睡觉。当然,我没法做一个让人睡着觉也能玩的游戏,所以我要另寻出路。

(一)主要对象的选择

与睡觉联系最为密切的当然是梦了,回想起来梦幻的东西似乎很容易让人放松,比如,我们列举一下比较有梦幻感的词语:星空、云朵、彩虹、城堡、极光、萤火虫、雪夜……是不是脑子里浮现出一些非常美好的画面?是不是感觉有那么一点点的放松?前些日子漫天柳絮飞舞,这让我想起小时候追着柳絮跑的情形,似乎小孩子对于这种能够飘来飘去却不太好捉到的东西有独特的青睐(但现在只觉着这玩意儿烦人),像柳絮、杨絮、蒲公英这种白色的、软绵绵的、能飘的东西,很容易跟云朵、蓝天联系在一起,这似乎也是个不错的解压的东西,所以就想是否可以模拟一下蒲公英(毕竟前些日子已经让柳絮弄得有点烦躁了)。蒲公英
但单纯的蒲公英并不能使整个游戏元素看起来比较丰满,所以我又开始在脑子搜寻梦幻有关的东西,然后我就想到了星空(其实几乎是第一个蹦出来念头)。如果说每个人都曾有一个遥不可及但是如梦如幻的梦想,那么星空无疑是人类诞生以来被惦记得最多的那个,不论是中国古代哲学中的黄岛十二星宿还是西方的十二星座,在伽利略造出望远镜之前,星空总是被赋予了一股神秘的力量,似乎星空中隐藏着万物本源的究极奥秘。而艺术家们对于星空的遐想也毫不吝啬,说烂了的神话(中西方都有)、梵高的《星空》以及诗人笔下落下九天的银河,都将星空与浪漫画上了等号。即便不说这些有的没的,我想每个孩子都曾有个摘星星的梦,所以将星空也考虑到系统里。

至此,我们这个小游戏的两个主角已经集齐,至于要怎么将其表现出来,我们来一点一点讨论。

(二)如何呈现蒲公英和星空

首先,让我们来看一下,艺术家们都是如何表现星空的。

梵高《星月夜》

艺术家果然是感性而浪漫的,他们眼中的星空都与我这个理工直女眼中的不一样。天气好的时候,我能看到的星空是这样婶儿的:
在这里插入图片描述
即便是思维差异如此之大,我们也能看出,不论在谁眼里,星星都是一个或大或小或明或暗的点,艺术家们将这个点放大、然后抽象化、再加上自己的想象,变成了他们作品中的那个样子,而星星本质上,还是那么一个点。但是上面这张图启发了我,如果单纯的星星是璀璨而浪漫的,那么星星与星星之间连线形成的星座就更多来几分神秘和哲理,而且这样的点线构成的画面还有着一种几何的美感。其实回归艺术的本质,无非是点、线、面的组合重构,康定斯基也说,

“点本质上是最简洁的形”、“点是时间上最简短的形”,“线是点的相对结果”[1];

而一个点在数学和力学上可以意味着很多,它可以有自己的质量、密度、初速度等等,一切推导都要从一个点开始,线则意味着点和点之间产生了某种关联(运动轨迹、力的作用等)。简单的点线组合已足以让我这个小游戏有无限的可能了,不论是从艺术的角度还是从力学的角度。至此,我已经将点线视为这个小系统中主要的操作对象了。

那么下一个问题来了:如何应用力学让点线呈现出梦幻感?

所有的梦境都是基于现实的扭曲和衍生,所以想要营造出梦幻感其实只要在现实的基础上略微抽象化一点就好。所以我们要先将星空模拟出来——这很好办,只需要在屏幕内随机生成点就好;然后将某些点用线连接,模拟出星座的样子;至于蒲公英的模拟,我们可以让散点聚集成一个圆,散点之间用线连接。下一步,我们要想办法让整个画面按照一定的规律动起来,这是实现梦幻感的关键,也是系统的主要部分。闲扯结束,下面我要上干货了。

三、具体代码实现

我们先看一下星星点的类:

class Mover {
    PVector loc;
    PVector vel;
    float bounce = 1.0;
    float r=2;

    boolean colliding = false;
    PVector acceleration;
    
    int num;

    Mover(PVector v, PVector l,int n) {
        vel = v.get();
        loc = l.get();
        acceleration = new PVector(0, 0);
        this.num=n;
    }
}

每个代表星星的点都是Mover类的一个实例,Mover中的重要属性有

  • 位置loc:二维向量,屏幕坐标
  • 速度vel:二维向量,对象在x、y方向的速度
  • 半径r:星星对象的半径
  • 加速度acceleration:二维向量,对象在x、y方向 的加速度
  • 序号num:当前星星对象的序号

初始化Mover对象的实例时,位置和速度都是随机的,加速度为0。当两个星星之间的距离小于100像素时,在两个星星之间连线,这样星星和星座都是动态的。效果如下:
动态星空
非常简单的一步,但是效果不错。简单的点线几何一旦动起来,竟会有一种极简的未来感,我想这也是近年扁平风流行的原因。

下一步,我们把鼠标也加进来,并且让星星们跟鼠标有点互动。mouseMover类:

class mouseMover{
  PVector location;
  PVector velocity;
  float r;
  int len=90;
  float k=0.002;
  
  int score=0;
  
  mouseMover(float x, float y,float r) {
    location = new PVector(x, y);
    this.r=r;
  }
}

鼠标对象是mouseMover类的一个实例,每颗星星与鼠标点之间都存在一个引力,并且根据这个引力改变星星的速度和加速度。这样一来,所有的星星都会有一个向着鼠标点运动的趋势,当星星与鼠标点的距离小于某个定值后,就会被鼠标点“捕捉”到,并跟随着鼠标运动。
引力比较好实现,只需要在Mover类里面添加一个函数,让每个Mover实例执行该函数即可。

  PVector attract(mouseMover m) {
    PVector force = PVector.sub(loc, m.location);             // Calculate direction of force
    float distance = force.mag();  
    distance = constrain(distance, 50.0, 10000.0);         
    force.normalize();   
      
    float strength=0;
    strength = -1*(6.77 * 30 ) / (distance * distance);      
    force.mult(strength);                                         // Get force vector --> magnitude * direction
    return force;
  }

但“捕捉”效果的实现并不顺利。首先,我给mouseMover类添加了一个弹力函数,当星星进入鼠标的捕捉范围时,该函数执行。

void connect(Mover b) {
  PVector force = PVector.sub(b.loc, this.location);
  float d = force.mag();
  float stretch = d - len;
  force.normalize();
  if(d>=len){
    force.mult(-20 * k * stretch);
  }
  else{
    force.mult(-1 * k * stretch);
  }
  b.applyForce(force);
}

可以看到,我将弹力中的拉力设置得比较大,而斥力比较小,是为了防止星星在做往复运动过于容易被甩出。但这样的问题是,星星很容易被吸引到鼠标中心而不是我希望出现的被捕捉后在某个范围内震荡。经过很多次尝试后,找到的结局办法是,控制最大速度,即当速度超过最大值时,将速度缩小到八成,这样在弹力的作用下,星星的运动范围有限,不会太靠近鼠标,也不会太容易逃离。
捕捉星星当星星被鼠标捕捉后,每颗星星还互相增加了一个斥力,不然随着鼠标的移动,星星会聚集到同一处。

 PVector push(Mover m) {
    PVector force = PVector.sub(loc, m.loc);    // Calculate direction of force
    float distance = force.mag();               // Distance between objects
    distance = constrain(distance, 50.0, 10000.0); 
    force.normalize();   // Normalize vector (distance doesn't matter here, we just want this vector for direction
      
    float strength=0;
    strength =0.5/distance;      
    force.mult(strength); // Get force vector --> magnitude * direction
    return force;
  } 

增加斥力后,捕捉到的星星会自动在鼠标周围均匀分布,效果还不错。捕捉到的星星与鼠标组成的动态图案是不是有点像一个跳动的蒲公英?

我想要摘下天上的星
聚成一束蒲公英
每一个都带着我许下的愿
轻轻地说给风儿听

最后我们用粒子系统添加上飘拂的蒲公英,蒲公英也是被鼠标点吸引的。

至此,游戏动画已经齐全。

但是作为一个游戏,没有任何评价机制是不行的。

四、玩法介绍

完整操作视频请点击链接:https://www.bilibili.com/video/av51170273/。

(一)开始游戏

开始游戏
左上角显示当前得分,初始为0;右上角显示剩余时间,初始为60.0。按SPACE/空格键开始游戏。

(二)游戏进行中

游戏进行中
玩家每捕捉到一颗星星+1分,注意躲避飘浮的蒲公英,每碰触到一次蒲公英-1分。
注意,如果鼠标移动速度过快,会将捕捉到的星星甩掉,分数也会丢失
在60s内尽可能多地得到分数。得分颜色会随分数高低改变。当当前分数<-10时,游戏提前结束。

(三)游戏结束

游戏结束
游戏结束后,屏幕中间会显示本轮得分,背景还可继续体验游戏,只是不再计算评分。

(作者最高得分记录83分,不知道有没有更厉害的朋友)

完整操作视频请点击链接:https://www.bilibili.com/video/av51170273/。

四、总结

我们来回顾一下,为了实现梦幻感星空和蒲公英的模拟,我都做了哪些事情

  1. 随机位置、随机速度的点动态模拟星星
  2. 当任意两颗星距离小于100时连线,动态模拟星座
  3. 粒子系统模拟飘浮的蒲公英
  4. 在鼠标与星星之间、鼠标与蒲公英之间增加引力,使其总是有向着鼠标运动的趋势。
  5. 当鼠标与星星之间的距离小于150时连线,星星被鼠标捕获,同时增加弹力,并控制星星的最大速度以实现震荡效果。
  6. 被捕获的星星之间增加斥力,相对均匀地分布在鼠标周围,并与鼠标一起组成蒲公英形状。

抽象的点线本身就具有未来感,一旦加上力学系统动起来之后,更有意想不到的效果。最终的捕捉效果我非常满意,拖着星星运动的感觉非常Q弹,还要小心翼翼地别把星星甩丢。星星自己的震荡效果也是多次调试之后确定的,最大运动速度过高会使星星太容易逃离,过低震荡范围又会太小,视觉效果不好,现在这样是最完美的状态了。最后颜色的选取也废了一点心思,尝试过一些特定的颜色,但是都有点单调,彩虹色更具有梦幻感,但饱和度太高会显得俗气,所以连线全部降低了饱和度,效果更加高级一点。为了设定颜色方便,colorMode改为HSB格式。

五、完整代码

已上传百度网盘:https://pan.baidu.com/s/1vkkChjMpGdvPb1h2xlAIyQ
提取码:q8bf

参考文献

[1] 瓦西里·康定斯基著,罗世平译. 点、线、面 [M]
[2] Daniel Shiffman著,周晗彬译. 代码本色:用编程模拟自然系统 [M]. 人民邮电出版社
[3] Processing官方参考文档

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值