Godot 游戏引擎学习 第七课

Godot 游戏引擎学习 第七课

December 25, 2020

今天是圣诞节,朋友圈里热热闹闹的让我感觉全世界都再过圣诞节,回家的一路上也格外冷清,相比都去购物中心或者吃喝玩乐一条街过圣诞节去了把。

今天继续学习 Godot,UP 今天讲的是实现很多动作游戏中的残影效果,对这个印象最深刻的是以前看黑客帝国时候,史密斯的那个躲子弹的残影太帅了,应该算是名场面了吧!好了,废话不多说,开干。

给角色添加残影

其实我之前看 Carotaa666 的直播的时候看到他说过这个思路,大概是通过计时器按一定的周期绘制一个精灵,然后再销毁掉。

这里 UP 主的思路也是一样的,先新建一个场景名为 Trail 再里面添加一个 Sprite 节点,和一个 AnimationPlayer,利用我们以前的学的,添加一个名为 fadeout 的渐隐动画,并让动画在一定时间后自我销毁,然后设置从清晰到模糊的渐变。

image-20201225220650839

然后给 Player 场景添加一个计时器,然后就是代码部分了。这里说到一个以后可能会经常用的函数 preload,具体看代码,我都写了 注释了。

# 连接计时器的Timerout信号	
func _on_TrailTimer_timeout():
	# 如果角色没有移动,则不绘制影子
	if velocity.x == 0:
		return
	
	# 用preload预加载我们做的那个影子场景,并实例化
	var trail = preload("res://src/effects/Trail.tscn").instance()
	# 把它添加进父场景
	get_parent().add_child(trail)
	# 改变它在场景树中的位置到player下方
	get_parent().move_child(trail, get_index())

	# 下面这些属性是要从角色上读取出来复制给影子的
	var properties = [
		"hframes",
		"vframes",
		"frame",
		"texture",
		"global_position",
		"flip_h"
	]
	# 从角色身上遍历,并给与影子
	for name in  properties:
		trail.set(name, sprite.get(name))

第一次了解 Shader

这里还说了 shader,什么是 shader?我也是第一次接触,以前玩绝地求生的为了改变画质用过一次,不过完全不知道是什么东西;

着色器(Shader)是用来实现图像渲染的,用来替代固定渲染管线的可编辑程序。其中 Vertex Shader 主要负责顶点的几何关系等的运算,Pixel Shader 主要负责片源颜色等的计算。

着色器替代了传统的固定渲染管线,可以实现 3D 图形学计算中的相关计算,由于其可编辑性,可以实现各种各样的图像效果而不用受显卡的固定渲染管线限制。

应该就是可以通过编程方式改变画面展现出来的效果,比如图片原本是彩色的,你可以通过 shader 改变运行后展示出来的颜色为灰色。shader 我看过一些 godot 的实现效果,只能说很强大,在实现一些特殊画面特效时非常有用,我还得多看看相关资料学习一下。

我们可以在 Sprite 上找到 Material,然后新建一个 ShaderMaterial > Shader,然后就可以编写着色器代码了。

具体代码如下:

# 定义shader类型,这个时固定的好像
shader_type canvas_item;

# 定义shader的外部参数,方便调试什么的,指定它的类型为hint_color,外部在设置的时候可以调用系统的取色板
uniform vec4 tint_color: hint_color = vec4(1.0);

# 这个是主入口函数,里面的内容就是在对每一个像素点的处理方式。
void fragment()
{
	# 在一个图片里每一个像素都有自己的坐标,这里是获取当前图片的颜色坐标
	vec4 col = texture(TEXTURE, UV);
	# 通过算法讲每个像素换算成灰色
	float grey = (col.r + col.g + col.b) / 3.0;
	# 这个UP说是色阶的处理,从多少过渡到多少(大概)
	grey = smoothstep(0.3, 0.6, grey) * 0.4 + 0.6;
	# 把计算出来的颜色给与贴图
	COLOR = vec4(grey * tint_color.rgb, col.a);
}

最后效果就是这样,还挺帅的。

第七课_trail_shader

增加飞行敌人

这里 UP 果然将 Slime 上一些共有属性提取到 Enmey 上去了。苍蝇作为怪物自然也要继承自 Enmey 脚本,并且也更改了移动方式。并通过一个 Area2D 检测敌人的范围。

image-20201226004615966

但是和 Slime 不一样的是,这个飞行怪物是会飞的,所以移动逻辑和地形 i 行走的完全不一样。也不会受 y 轴的重力影响,所以这里代码部分改了很多。而且还有警戒区域.

这里有一些代码有点一知半解

extends "res://src/enemies/Enemy.gd"

var target_posistion = null

onready var player_sensor = $PlayerSensor


func _process(delta):
    if is_dead:
        # 死后不在移动
        velocity.x = 0
        # 且增加增加重力影响
        velocity.y += gravity * delta
    else:
        # 计算并更新目标位置
        target_posistion = _calc_target_position()
        if target_posistion == null:
            # 如果没有新的目标位置就只移动到目标位置去
            velocity = velocity.move_toward(Vector2.ZERO, acceleration * delta)
        else:
            # 获取自身到目标位置的向量
            var direction = global_position.direction_to(target_posistion)
            # 根据目标向量进行移动
            velocity = velocity.move_toward(direction * max_speed, acceleration * delta)
            # 转身
            sprite.flip_h = direction.x > 0
  
func _physics_process(_delta):
    # 移动
    velocity = move_and_slide(velocity)

func _calc_target_position():
    # 获得所有碰撞区内的对象数组,因为只有有些角色能和他碰撞,所以只要有数据就是游戏角色
    var bodies = player_sensor.get_overlapping_bodies()
    #如果有的话就更新目标位置
    if not bodies.empty():
        # 返回新的目标位置
        return bodies[0].global_position + Vector2(0, 50) 
    # 这里大概意思如果离目标距离太远也不追踪了,不过不知道为什么要设置成25
    if target_posistion != null and global_position.distance_squared_to(target_posistion) < 25:
        print(global_position.distance_squared_to(target_posistion))
        return null
	
    return target_posistion

好了,大概效果如下

第七课_fly_enemy

加入评论