当贪吃蛇碰上剪刀石头布——godot版(用line2D)

        

目录

1、新建项目

2、设计游戏画面

3、添加玩家和移动脚本

4、添加食物场景和食物脚本

5、吃与被吃的实现

被吃的逻辑

吃到的逻辑

6、变长变长再变长


        我们使用godot来开发一款家喻户晓的贪吃蛇小游戏,结合剪刀石头布规则:玩家和食物都有三种属性:剪刀、石头和布;
        游戏规则:当玩家是剪刀的时候,只能吃掉剪刀和布,石头会来追捕玩家。吃掉剪刀和布会变长,吃掉布还会变成布,这时可以反过来吃掉石头。被克制属性追到或者撞到墙上会被击杀。灵感是洗澡(*^▽^*)催发的~。

        我们先完成贪吃蛇的基础玩法:蛇移动,吃掉食物变长。

下面是gif游戏图:

1、新建项目

在左上角的“项目——项目设置——窗口——拉伸”,我们设置模式为2d,比例为keep,这样子全屏也不会让游戏被拉伸了

2、设计游戏画面

        如图所示,新建Control根节点节点,重命名为"UI";在UI节点下新建ColorRect,重命名为BG,设置颜色偏黑色;在UI节点下新建CanvasLayer节点用来显示分数,CanvasLayer节点下新建Label节点,重命名为Score(CanvasLayer节点默认z值为1,也就是在图片等节点之上,避免图片覆盖了分数)。

        然后点击Score节点,Control——Theme Overrides——Fonts:新建DynamicFont,点击Font将我们的字体资源文件 Comfortaa-Bold.ttf 拖进去,并在Settings中改变字体大小等。

这是初始游戏画面:

3、添加玩家和移动脚本

        如图所示,新建场景,新建“KinematicBody2D”根节点,重命名为Player,新建两个子节点:ColorRect和CollisionShape2D,ColorRect的颜色赋上自己喜欢的并且居中;CollisionShape2D新建Shape,把ColorRect覆盖住。

然后Player新建脚本Player.gd:

extends KinematicBody2D

#移动方向和移动速度 ,export让速度可以随时更改
var direction = Vector2(0,0)
export var speed = 100

func _ready():
	pass # Replace with function body.
	
# 用物理刷新来移动,避免delta值不同使得位移不同。
func _physics_process(delta):
	if Input.is_action_pressed("ui_up"):
		direction = Vector2.UP
	if Input.is_action_pressed("ui_down"):
		direction = Vector2.DOWN
	if Input.is_action_pressed("ui_left"):
		direction = Vector2.LEFT
	if Input.is_action_pressed("ui_right"):
		direction = Vector2.RIGHT
	
	position += direction * speed * delta

通过注释不难理解。现在我们把玩家放在UI场景中:

4、添加食物场景和食物脚本

类似构建Player场景,新建Food场景:

并且为Food添加脚本,Food.gd:

extends KinematicBody2D

#吃掉食物的价值
var value
# 死亡发出信号,修改分数等操作
signal die

5、吃与被吃的实现

首先让我们在左上角点击项目——项目设置——层名称——2D物理,新建三个层次:World、Player和Food

        Player场景中,根节点的Collision的Layer我们设置为Player,Mask设置为Food(Layer可以理解为所在的层次,Mask可以理解为要与哪个层次发生碰撞)。Food场景同理。

被吃的逻辑

        然后在Player场景中,添加“Area2D”子节点,重命名为EatArea2D,并且添加“CollisionShape2D”节点然后新建Shape,把ColorRect覆盖住。Food场景也是这个操作,“Area2D”重命名为HurtArea2D。然后点击HurtArea2D,在右边的“节点”——“信号”我们双击“area_entered(area:Area2D)”信号,连接到Food所在的脚本。

这个信号是当玩家触碰到食物的时候会调用的,当触碰到食物的时候,我们传递死亡信号表示玩家吃到了食物,并且销毁食物场景。

吃到的逻辑

我们修改Player的代码:

extends KinematicBody2D

#移动方向和移动速度 ,export让速度可以随时更改
var direction = Vector2(0,0)
export var speed = 100
var player_score

onready var Food = preload("res://Food.tscn")
onready var score = $"../CanvasLayer/Score"
onready var color_rect = $ColorRect

func _ready():
	randomize() #randomize()是用来修改随机种子数
	create_food()
	player_score = 0
	
# 用物理刷新来移动,避免delta值不同使得位移不同。
func _physics_process(delta):
	if Input.is_action_pressed("ui_up"):
		direction = Vector2.UP
	if Input.is_action_pressed("ui_down"):
		direction = Vector2.DOWN
	if Input.is_action_pressed("ui_left"):
		direction = Vector2.LEFT
	if Input.is_action_pressed("ui_right"):
		direction = Vector2.RIGHT
	
	position += direction * speed * delta

func create_food():
	var food_tscn = Food.instance()
	# call_deferred是在空闲时间调用方法,避免由于节点忙碌调用失败
	get_parent().call_deferred("add_child", food_tscn)
	set_food_position(food_tscn)
	
	food_tscn.value = 1
	food_tscn.connect("die",self,"change_score",[food_tscn.value])
	
func set_food_position(food_tscn):
	var random_x = 0
	var random_y = 0
	var food_size = food_tscn.get_node("ColorRect").rect_size
	var player_size = color_rect.rect_size
	var win_size = Vector2(get_viewport().size.x,get_viewport().size.y)
	var overlap = true
	
	# 循环,直到找到不与蛇重叠的食物位置
	while overlap:
		random_x = randi() % (int(win_size.x - food_size.x)) + food_size.x/2
		random_y = randi() % (int(win_size.y - food_size.y)) + food_size.y/2
		var food_rect = Rect2(random_x - food_size.x, random_y - food_size.y, food_size.x, food_size.y)  # 食物的碰撞区域
#
#		# 检查食物是否与玩家的身体重叠
		overlap = false
		var player_rect = Rect2(global_position - Vector2(player_size.x/2, player_size.y/2), Vector2(player_size.x, player_size.y))
		if food_rect.intersects(player_rect):
			overlap = true
	
	food_tscn.position = Vector2(random_x, random_y)
	
func change_score(value):
	# 增加分数并且生成新的食物。
	# create_food()这个方法随时都可以调用,这意味着我们可以用计时器调用,也可以为玩家创建技能生成食物来调用
	player_score += value
	score.text = "SCORE: " + str(player_score)
	create_food()

        可以看到,在create_food()方法中,我们新建food之后,进行信号连接,die这个信号发送之后会调用change_score(value)方法,这个时候我们可以来回增加分数并且生成新的食物了:

6、变长变长再变长

传统的贪吃蛇变长需要使用列表存储,反复移动列表中的元素使得移动的时候尾巴转弯是正确的,我们在这次开发中使用Line2D来模拟尾巴,实现拖尾效果。

        优点是简单快速并且能自己改变形状。

        缺点是长度会受到移动速度的影响。

新建Line2D的场景,将其拖到UI场景中,Line2D新建曲线,设置如下:

在Player.gd中,我们添加了create_tail()函数在_physics_process(delta)中调用,长度是在change_score(value)中变长的。

extends KinematicBody2D

#移动方向和移动速度 ,export让速度可以随时更改
var direction = Vector2(0,0)
export var speed = 100
export var player_length = 0
var player_score
var player_size

onready var Food = preload("res://Food.tscn")
onready var score = $"../CanvasLayer/Score"
onready var color_rect = $ColorRect
onready var line_2d = $"../Line2D"

func _ready():
	randomize() #randomize()是用来修改随机种子数
	player_score = 0
	player_size = color_rect.rect_size
	create_food()
	
	
# 用物理刷新来移动,避免delta值不同使得位移不同。
func _physics_process(delta):
	if Input.is_action_pressed("ui_up"):
		direction = Vector2.UP
	if Input.is_action_pressed("ui_down"):
		direction = Vector2.DOWN
	if Input.is_action_pressed("ui_left"):
		direction = Vector2.LEFT
	if Input.is_action_pressed("ui_right"):
		direction = Vector2.RIGHT
	
	position += direction * speed * delta
	
	# 制作尾巴
	create_tail()

func create_tail():
	# 在line2D中,是没有长度这个概念的,只有宽度,而我们在生成点的同时把最早生成的点删掉,就有长度的产生了
	line_2d.width = color_rect.rect_size.x
	line_2d.add_point(self.position)
	if(line_2d.get_point_count() >= player_length):
		line_2d.remove_point(0)

func create_food():
	var food_tscn = Food.instance()
	# call_deferred是在空闲时间调用方法,避免由于节点忙碌调用失败
	get_parent().call_deferred("add_child", food_tscn)
	set_food_position(food_tscn)
	
	food_tscn.value = 1
	food_tscn.connect("die",self,"change_score",[food_tscn.value])
	
func set_food_position(food_tscn):
	var random_x = 0
	var random_y = 0
	var food_size = food_tscn.get_node("ColorRect").rect_size
	var win_size = Vector2(get_viewport().size.x,get_viewport().size.y)
	var overlap = true
	
	# 循环,直到找到不与蛇重叠的食物位置
	while overlap:
		random_x = randi() % (int(win_size.x - food_size.x)) + food_size.x/2
		random_y = randi() % (int(win_size.y - food_size.y)) + food_size.y/2
		var food_rect = Rect2(random_x - food_size.x, random_y - food_size.y, food_size.x, food_size.y)  # 食物的碰撞区域
#
#		# 检查食物是否与玩家的身体重叠
		overlap = false
		var player_rect = Rect2(global_position - Vector2(player_size.x/2, player_size.y/2), Vector2(player_size.x, player_size.y))
		if food_rect.intersects(player_rect):
			overlap = true
	
	food_tscn.position = Vector2(random_x, random_y)
	
func change_score(value):
	# 增加分数并且生成新的食物。
	# create_food()这个方法随时都可以调用,这意味着我们可以用计时器调用,也可以为玩家创建技能生成食物来调用
	player_score += value
	score.text = "SCORE: " + str(player_score)
	player_length += 1
	create_food()

呼呼,我们的最终结果是下面的gif:
 


之后我们的剪刀石头布会用到状态机,下次见~

补充,这是后续的最终版实现教程:

当贪吃蛇碰上剪刀石头布——godot版(用line2D)最终版!-CSDN博客

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值