Gamemaker studio2经验(3)——昼夜更替效果实现

问题概述

在一些知名引擎(如unity,godot,cocos等)中,天空盒概念的引入使得场景中的天空丰富多彩,但是gm就吃了这个大亏。由于动态天空制作麻烦、没有对口的天空盒系统,导致很少有开发者在gm中制作昼夜交替效果。
昨天因为被千字大论文恶心到了,所以上gm研究了下DynamicSky的插件,发现原理其实很简单,于是想在这里写一写,鼓励大家亲自动手做一下这个效果。

制作流程

一、绘制静态天空图

用gm的script来写shader是个方法,但是shader对于普通爱好者们来说实在有点难,所以基础图像我们就用PS人为绘制一下就可以。(这个真的很简单很简单,直接使用PS的渐变效果就出来了,这个再不会我也没有办法啦~)
以下我们制作六张图:
日出(渐变色:橙色->深蓝,主要描绘太阳初升时的色调)
清晨(渐变色:中蓝->深蓝,太阳光辉减弱,天还没有亮完)
正午(渐变色:浅蓝->中蓝,天亮了)
日落(渐变色:浅蓝,这是一天最亮的时候,要体现出明媚的感觉)
傍晚(渐变色:青色->深蓝,太阳下山后的情景)
深夜(渐变色:黑色,但建议不要纯黑)

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
(嘤嘤嘤,人家不是纯黑啦~)

二、背景控制

首先我们创建一个控制器obj_BackgroundController,接下来会逐层介绍它的使用方法。
obj_BackgroundController—Create层:

Trancicion[0]=0
Trancicion[1]=0
Trancicion[2]=1
Trancicion[3]=0
Trancicion[4]=0
Etapa=3
depth=10;

Trancicion[]是一个用来描绘各个天空盒强度的数组。这里我们对它的下标进行定义:0表示日出,1表示清晨,2表示正午,3表示下午,4表示傍晚。那么Trancicion[2]=1表示的就是“正午这张天空的强度是最大的,其他天空的强度为0”,理解为此时的天空盒就是正午的天空。
Etapa是表示天空切换阶段的。天空切换阶段表示现在是由哪个天空盒向哪个天空盒转变。例如Etapa=3就表示从正午向下午的图像过渡,Etapa=4表示从下午向傍晚的图像过渡,以此类推。
depth=10的作用是让背景镶嵌在游戏的底层,这个无需多言。

obj_BackgroundController—Step层:

//Trancicion starts at 2:15 Am
if global.Hs=4 && global.Min=15{Etapa=1}
//Trancicion strat at 6:35 Am
if global.Hs=7 && global.Min=35{Etapa=2}
//Trancicion starts at 11:25 Am
if global.Hs=11&& global.Min=25{Etapa=3}
//Trancicion Starts at 15:45 Pm
if global.Hs=15 && global.Min=45{Etapa=4}
//Trancicion Starts at 20:05 Pm
if global.Hs=20 && global.Min=5{Etapa=0}

//Sun Rice
if Etapa=1{   
   if Trancicion[0]<1{Trancicion[0]+=0.00025}else{Trancicion[0]=1}//Increase the alpha value  +0.000125 per step
   if Trancicion[4]>0{Trancicion[4]-=0.00025}else{Trancicion[4]=0}//decrease the alpha value  -0.000125 per step

}

//Morning
 if Etapa=2{   
    if Trancicion[1]<1{Trancicion[1]+=0.000125}else{Trancicion[1]=1}//Increase the alpha value  +0.000125 per step
    if Trancicion[0]>0{Trancicion[0]-=0.000125}else{Trancicion[0]=0}//decrease the alpha value  -0.000125 per step
}

// Midday
  if Etapa=3{ 
    if Trancicion[2]<1{Trancicion[2]+=0.000125}else{Trancicion[2]=1}//Increase the alpha value  +0.000125 per step
    if Trancicion[1]>0{Trancicion[1]-=0.000125}else{Trancicion[1]=0}//decrease the alpha value  -0.000125 per step
}

//Afternoon
  if Etapa=4{  
    if Trancicion[3]<1{Trancicion[3]+=0.000125}else{Trancicion[3]=1}//Increase the alpha value  +0.000125 per step
    if Trancicion[2]>0{Trancicion[2]-=0.000125}else{Trancicion[2]=0}//decrease the alpha value  -0.000125 per step   
}

//Nightfall
  if Etapa=0{
    if Trancicion[4]<1{Trancicion[4]+=0.000125}else{Trancicion[4]=1}//Increase the alpha value  +0.000125 per step
    if Trancicion[3]>0{Trancicion[3]-=0.000125}else{Trancicion[3]=0}//decrease the alpha value  -0.000125 per step
}

看着麻烦,实际上非常好理解。第一段是通过判定时间来更改Etapa,比如到了原来是从清晨向正午转变,而到了11:25时,Etapa切换为3,那么久变成了正午向下午转变。
第二段就是对Etapa的解释。既然Etapa=3表示从正午向下午转变,那么实际操作是什么样的呢?自然就是降低正午天空的透明度,而增强下午天空的透明度了。这里面的0.000125表示每一帧所更改的透明度,需要详细计算,它的计算方法是:
1/(切换Etapa间隔时间(秒)*FPS(即帧速))
这部分相信具有一定代码功底的人一眼就能看明白。

obj_BackgroundController—Draw层:

draw_background_ext(BK_NightFall,__view_get( e__VW.XView, 0 ),__view_get( e__VW.YView, 0 ),(room_width/64),(room_height/64),0,image_blend,Trancicion[4])

draw_background_ext(BK_AfterNoon,__view_get( e__VW.XView, 0 ),__view_get( e__VW.YView, 0 ),(room_width/64),(room_height/64),0,image_blend,Trancicion[3])

draw_background_ext(BK_MidDay,__view_get( e__VW.XView, 0 ),__view_get( e__VW.YView, 0 ),(room_width/64),(room_height/64),0,image_blend,Trancicion[2])

draw_background_ext(BK_Morning,__view_get( e__VW.XView, 0 ),__view_get( e__VW.YView, 0 ),(room_width/64),(room_height/64),0,image_blend,Trancicion[1])

draw_background_ext(BK_SunRice,__view_get( e__VW.XView, 0 ),__view_get( e__VW.YView, 0 ),(room_width/64),(room_height/64),0,image_blend,Trancicion[0])

既然每个图的Trancicion已经得到了,那么就依次绘制他们吧。
注意:draw_background_ext是用脚本重载过的函数,具体写法用draw_sprite_ext即可

三、时间控制器

在这里我们再创建一个对象叫obj_Control用来控制场景中的时间。
obj_Control—create层:

global.Seg=0;                       //Seconds
global.Min=26;                       //Minutes
global.Hs=11;                        //Hours
global.Dia=1                        //Day number

obj_Control—step层:

//Seconds
if global.Seg < 30 {global.Seg+=1}else{global.Seg=0;global.Min+=1}

//Minutes
if global.Min >= 60 {global.Min=0;global.Hs+=1}

//Hours
if global.Hs  >=24  {global.Hs=0;global.Dia+=1}

//Days
if global.Dia >7   {global.Estacion+=1;global.Dia=1}

这个就是一个特别简单的时间控制器,算是C语言中基础中的基础了,不想多解释什么。
需要特别注意的是,在create层中我们初始化了时间,那么这个初始化的时间应该和背景控制那里面的背景初始值及Etapa相对应。比如我在背景控制那里预设了Trancicion[2]=1表示正午开始,那么我们就最好把时间预设为11:25(即Etapa对应的转换时间结点)

四、深夜特殊处理

为什么深夜的天空盒要单独拿出来做?因为深夜的图太“黑”了,与其他图像产生了冲突(叠在一起会变得巨黑,然后啥也看不见了)
所以我们创建一个新对象叫obj_DeepNight,用来专门控制黑夜。
obj_DeepNight—create层:

Etapa=0 //This Variable is for changing the trancicion moment
Alpha=0 //This Variable  is to manage the alpha value in the background
depth=10;

obj_DeepNight—step层:

if global.Hs>=19 && global.Min=45{Etapa=1}
if global.Hs >=4 && global.Min=35{Etapa=0}
if Etapa=1{if Alpha<0.9{Alpha+=0.00025}else{Alpha=0.9}};
if Etapa=0{if Alpha>0{Alpha-=0.00025}else{Alpha=0}};

obj_DeepNight—draw层:

draw_background_ext(BK_Night,__view_get( e__VW.XView, 0 ),__view_get( e__VW.YView, 0 ),__view_get( e__VW.WView, 0 )/64,__view_get( e__VW.HView, 0 )/64,0,image_blend,Alpha)

如果你认真看了obj_BackgroundController那里的代码解读,那么你就会发现这个DeepNight和它没什么差别。只是在显示这张图的时候,它的参数与obj_BackgroundController所控制的参数有点区别,这也是单独把它拿出来的原因。

五、(选作)繁星点缀

既然都做到这里了,不如送一个bonus吧。刚才我们提到深夜的图太黑了,未免会显得有些单调,但是如果有繁星点缀,那效果一定很棒呢!
老规矩,我们创建一个obj_Stars,具体精灵自己画也行网上找也行。

obj_Stars—create层:

A=choose(0,-.1,-.2,-.3,-.4,-.5)
//Stage for Image Alpha Controler
Stage=0
//Alpha First Value
Alpha=A
//Max Alpha Value
MaxAlpha=0.85
//Stop Animation
image_speed=0;
//Animation Controller
alarm[0]=irandom_range(120,180)

注释标注的很清晰,这里就是对星星属性的初始化。
A决定了某颗星星的初始透明度,
stage表示星星是由明到暗还是由暗到明(受具体时间控制),
Alpha顾名思义,就是星星的透明度
alarm[0]是计时器,一会儿会解释它的作用。

obj_Stars—step层:

if global.Hs=20 && global.Min=45{Stage=1}
if global.Hs=3 && global.Min=(45+(A*50)){Stage=0}
if Stage=1{if Alpha<MaxAlpha{Alpha+=0.00025}else{Alpha=MaxAlpha}};
if Stage=0{if Alpha>0{Alpha-=0.00025}else{Alpha=A}};

天黑则现,天明则失,这个代码已经诠释了这一点。

ovj_Stars—Alarm[0]层:

if irandom(10)==1{image_speed=0.2}
alarm[0]=irandom_range(120,160)

这个操作为了让群星闪动的速度发生改变,从而避免出现“所有星星同频闪烁”的奇怪现象。
当然后续还可以做一些例如星星移动、碰撞、闪现等你喜欢的效果。这里只是描述一下基础的操作。

六、收工/结语

最后我们直接把obj_BackgroundController、obj_Control和obj_DeepNight放置到场景中即可,然后就享受昼夜更替的效果吧!
其中有几个难点需要特别注意一下,一个是每帧透明度的改变量,因为每个人做的游戏FPS并不能保证都是60,所以最好查看一下FPS的具体值,然后再套用公式。不过有一说一,就算计算结果有一点点偏差,肉眼也丝毫看不出来有什么问题。
还有就是obj_Control中的预设时间与obj_BackgroundController中的初始状态时各个天空图的比重(即Trancicion[]数组)还有Etapa的适配。假如适配错误,真有可能会出现半夜12点日出的恐怖景象哦!!

附:

obj_Control中的draw层,用于实时绘制时间

draw_set_alpha(1)
draw_set_colour(c_white)
draw_text(__view_get( e__VW.XView, 0 )+10,__view_get( e__VW.YView, 0 )+14,string_hash_to_newline("Day:"+string(global.Dia)))
draw_text(__view_get( e__VW.XView, 0 )+10,__view_get( e__VW.YView, 0 )+54,string_hash_to_newline("Time:"+string(global.Hs)+" : "+string(global.Min)))

draw_background_ext函数的重构(需要写到script里)

draw_sprite_ext(argument0, 0, argument1, argument2, argument3, argument4, argument5, argument6, argument7);
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值