Godot实用代码1000例

关于

这是《Godot实用代码1000例》的CSDN博客版本(之前是PPT版本),将收录笔者自己探索和学习的一些简单Godot编程案例。

文章目录

  • 关于
    • 原版开篇的废话
    • 适合哪些人群
    • 建议
    • 前置知识
  • 基础的移动、旋转操作
    • 图标跟随鼠标移动
    • 图标跟随鼠标旋转(1)
    • 图标跟随鼠标旋转(2)
    • rotation、rotation_degrees、rotate()
      • Node2D的rotate()方法
      • Vector2有一个rotated()方法,注意区分
    • 图标不断自转
      • @GDScript
    • 一个图标围绕另一个为中心旋转
      • 向量加减法
        • 加法
        • 减法
      • 与(b-a)相关的几个Vector2方法和写法
        • b.angle_to_point(a) = (b-a).angle()
        • a.direction_to(b) = (b-a).normalized() = (b-a)/(b-a).length()
    • 一个不断旋转的炮塔
      • 炮筒随鼠标位置围绕炮身移动
  • RTS相关
    • 实现图标在两个位置点循环往复移动(RTS巡逻)
    • 简单的建筑物集结点设置和造兵
    • 简单的无限制放置建筑物
    • 基于Tilemap的基础地图编辑
    • 基于Tilemap的基础地图编辑(多图块编辑)
      • AtlasTexture的引入
    • 获取TileMap的TileSet,以及其中的Tile的ID
    • 点击物体进行拖放
  • 视图操作
    • 简单的鼠标滚轮缩放视图
    • 鼠标左键拖动视图
    • Ctrl+鼠标滚轮旋转视图

原版开篇的废话

Godot最新的官方文档已经实现API中文化,这是一个很好的时机去进行再次深入的学习。Godot依然缺乏或系统或循序渐进的基础教程,而我们即将迎来2022——新的一年。
笔者认为模仿式学习和探索式学习结合是自学Godot的最佳方式。

  • 模仿式学习:抄写代码、跟做实例
  • 探索式学习:查API、查文档,探索可能性

学习是一个很主观的事情,尤其是自学。自学要持久,兴趣是第一要素。自学要循序渐进,更要厚积薄发。
在基础阶段进行模仿,在进阶阶段开始探索。 Godot实用代码1000例就是基于让初学者在基础阶段模仿为目的而创建的,当然越到后面的例子可能会越具有一些复杂性,这也会促使学习者不断进步。笔者自身也是Godot的万千新人学习者之一,经历了将近一年半的学习,并且编写有自己的插件和几份笔记文档,在B站也发过几个系列视频。本文档就来自于2022年初,其中大多数例子是自己随着自己兴趣写的。当然也参照和吸纳了不少官方文档,以及视频、文章以及Godoter群友们的优秀实例和代码。本文档会尽量参照官方文档给与的准确定义和示例,并且会引用笔者之前总结的文档中的内容。
本文档将以开放形式分享,任何Godoter爱好者、学习者都可以自由使用。
后期会创建word和pdf版本,链接也会放出来,供大家离线查看和使用。

适合哪些人群

  • 本教程中的内容跨度会比较大,案例的复杂度不一。
  • 但是会尽量在贴出操作过程和代码的同时,解释关键知识点。
  • 因此,本教程适合懂一些Godot基础和有一些Gdscript编程经验的Godot爱好者

建议

  • 建议基础入门的童鞋可以按内容顺序由上到下,由简入繁的学习,并且关注于对基础操作和知识点的掌握和理解
  • 建议基础扎实的童鞋略过简单事例,通过目录检索直奔主题,去了解你感兴趣的,而不是乏味的重复

前置知识

  • Godot是什么?能做什么?
  • Godot基础的界面操作和基础的Gdscript使用如:节点的引用、变量申明、基础的数据类型,节点类型等。
  • 任何编程经验都是有益的,任何其他游戏引擎使用和游戏编写经验也是有用的

基础的移动、旋转操作

图标跟随鼠标移动

本例实现一个Sprite跟随鼠标移动的效果。

extends Node2D

onready var icon = $icon

func _process(delta):
	icon.position = get_global_mouse_position()

图标跟随鼠标旋转(1)

extends Node2D

onready var icon = $icon

func _process(delta):
	icon.look_at(get_global_mouse_position())

图标跟随鼠标旋转(2)

根据之前的知识点,我们通过Sprite的rotation属性来实现一个与内置look_at()方法一样的效果:

extends Node2D

onready var icon = $icon

func _process(delta):
	icon.rotation =  get_global_mouse_position().angle_to_point(icon.position)
extends Node2D

onready var icon =$icon
func _process(delta):
	icon.rotation= icon.position.direction_to(get_global_mouse_position())angle()
extends Node2D

onready var icon = $icon
func _process(delta):
	icon.rotation= (get_global_mouse_position()-icon.position).angle()

可以看到我们可以不止有一种写法,但是无论如何,我们其实都是在使用Vector2提供的方法.而且笔者采用了又臭又长的写法,通过简单的变量定义和引用,代码将变得更加清晰易懂。
Godot实用代码1000例_第1张图片

rotation、rotation_degrees、rotate()

除了使用rotation属性之外,还可以使用姊妹属性rotation_degrees,两者都代表2D节点相对于自己的初始朝向(水平向右)旋转的角度,逆时针为负数,顺时针为正数,只不过两者采用的单位不一样,rotation属性以弧度为单位,rotation_degrees以度为单位

Node2D的rotate()方法

Godot实用代码1000例_第2张图片

Vector2有一个rotated()方法,注意区分

Godot实用代码1000例_第3张图片
Godot实用代码1000例_第4张图片

图标不断自转

依然是旋转,不过这次我们让Sprite不断的自转

extends Node2D

onready var icon = $icon

func _process(delta):
	icon.rotate(degzrad(10))

这里我们用到了Node2D自身的rotate方法,并且为了直观,采用度数,并用GDS内置函数deg2rad()将度数转化为弧度。

等价写法:

extends Node2D

onready var icon =  $icon

func _process(delta):
	icon.rotation += deg2rad(10)

@GDScript

Godot实用代码1000例_第5张图片
Godot实用代码1000例_第6张图片

一个图标围绕另一个为中心旋转

这是利用偏移的方法

extends Node2D

onready var icon =  $icon
onready var icon2 = $icon2

func _process(delta):
	icon.position = icon2.position # 设定两个Sprite的位置重合
	icon.0ffset.× = 200 # 设定围绕物体距离被围绕物体的距离--围绕物体位置X向偏移
  	icon.rotate(deg2rad(1)) # 围绕物体围绕中心点旋转

下面是利用向量旋转和加减法实现

extends Node2D

onready var icon= $icon
onready var icon2 =$icon2

func _process(delta):
	var a:Vector2 =icon.position
	var b:Vector2 =icon2.position
	icon2.position = a + (b-a).rotated(deg2rad(1))

Godot实用代码1000例_第7张图片

向量加减法

Godot实用代码1000例_第8张图片

加法

三角形法则:AB+BC=AC,这种计算法则叫做向量加法的三角形法则,简记为:首尾相连、连接首尾、指向终点。

减法

AB-AC=CB,这种计算法则叫做向量减法的三角形法则,简记为:共起点、连终点、指被减。
在这里插入图片描述
Godot实用代码1000例_第9张图片

与(b-a)相关的几个Vector2方法和写法

(b-a)
基础的向量减法,返回的是由A点指向B点的向量

b.angle_to_point(a) = (b-a).angle()

Godot实用代码1000例_第10张图片
b.angle_to_point(a)简记为:B在A的什么方向,仍然是返回一个由A指向B的向量,当然这里不是直接返回这个向量,而是返回它与X轴正方向的夹角

a.direction_to(b) = (b-a).normalized() = (b-a)/(b-a).length()

Godot实用代码1000例_第11张图片
Godot实用代码1000例_第12张图片

一个不断旋转的炮塔

这其实是一个子节点围绕父节点进行旋转的问题。

extends Node2D

onready var body = $炮身
onready var paota = $炮身/炮塔

func _process(delta):
	paota.position=Vector:2.ZER0 # 设定子物体的位置为父物体的坐标原点--也就是两者中心重合
	paota.offset.X=48 # 设定围绕物体距离被围绕物体的距离--围绕物体位置X向偏移
	paota.rotate(deg2rad(1)) # 围绕物体围绕中心点旋转

炮筒随鼠标位置围绕炮身移动

我们只需要稍微结合一下上面学到的get_global_mouse_position()来获取鼠标位置,就可以让炮塔的炮筒随着我们的鼠标位置进行旋转。而下一步你只要实现点击发射炮弹,一个简单的设计游戏不就来了?

extends Node2D

onready var body=$炮身
onready var paota=$炮身/炮塔

func _process(delta):
	paota.position=Vector:2.ZER0 # 设定子物体的位置为父物体的坐标原点--也就是两者中心重合
	paota.offset.X=48 # 设定围绕物体距离被围绕物体的距离--围绕物体位置X向偏移
	paota.Look_at(get_global_mouse_position()) # 炮筒随鼠标位置围绕炮身移动

RTS相关

实现图标在两个位置点循环往复移动(RTS巡逻)

extends Node2D

onready var icon =$icon

var start_positon:Vector2=Vector:2(100,100)#起始位置
var end_positon:Vector2=Vector:2(300,300)#结束位置
var speed:float=5.0#速度

func _ready()
	icon.position = start_positon
	
func _process(delta):
	if icon.position.distance_to(end_positon)>0:
		icon.position = icon.position.move_toward(end_positon,speed)
	else:
	#起始位置与结束位置互换
	var old_start_positon = start_positon
	start_positon = end_positon
	end positon = old_start_positon

简单的建筑物集结点设置和造兵

Godot实用代码1000例_第13张图片

extends Node2D

onready var soldier = $兵
onready var Camp = $兵营
onready var Flag = $旗帜

var new_soldier:KinematicBody2D # 新士兵
var can_move = false
var speed:float =100.0 # 移动速度
var velocity = Vector2.ZERO

func _ready():
	Flag.hide()
	soldier.hide()
	
func _input(event):
	if event is InputEventMouseButton and event.pressed:
		if event.button_index == BUTTON_LEFT:
			Flag.hide()
		if event.button_index == BUTTON_RIGHT:
			Flag.position = get_global_mouse_position() # 将鼠标点击位置设为集结点
			Flag.show()

func _process(delta):
	if can_move:
		if new_soldier.position.distance_to(Flag.position)>5:
			var dir = new_soldier.position.direction_to(Flag.position) # 方向
			velocity = dir * speed
			velocity = new_soldier.move_and_slide(velocity)
		else:
			new_soldier = null
			can_move =false



func _on_TextureButton_pressed():
	var n_soldier= soldier.duplicate()# 创建新的士兵
	add_child(n_soldier)
	n_soldier.position = Camp.position# 位于营地中心
	n_soldier.show()
	new_soldier = n_soldier
	can_move = true
	pass

简单的无限制放置建筑物

extends Node2D

var currnt_build # 当前要建造的建筑物
var can_build = false # 开启建筑物放置的标记变量

onready var fangBtn = $CanvasLayer/Panel/HBoxContainer/fangBtn
onready var sanjiaoBtn = $CanvasLayer/Panel/HBoxContainer/sanjiaoBtn
onready var yuanBtn = $CanvasLayer/Panel/HBoxContainer/yuanBtn


func _input(event):
	if event is InputEventMouseButton and event.pressed:
		if event.button_index == BUTTON_LEFT:
			if can_build:
				var t_build = currnt_build.duplicate()
				add_child(t_build)
				t_build.position = get_global_mouse_position()
				t_build.modulate.a = 1.0 # 透明度变为100%
				currnt_build.queue_free()
				can_build = false

func _process(delta):
	if can_build:
		currnt_build.modulate.a = 0.5 # 透明度变为50%
		currnt_build.position = get_global_mouse_position()
		

func _on_fangBtn_pressed():
	var ns = Sprite.new()
	ns.texture = fangBtn.texture_normal
	add_child(ns)
	currnt_build = ns
	can_build = true
	pass


func _on_sanjiaoBtn_pressed():
	var ns = Sprite.new()
	ns.texture = sanjiaoBtn.texture_normal
	add_child(ns)
	currnt_build = ns
	can_build = true
	pass


func _on_yuanBtn_pressed():
	var ns = Sprite.new()
	ns.texture = yuanBtn.texture_normal
	add_child(ns)
	currnt_build = ns
	can_build = true
	pass

Godot实用代码1000例_第14张图片
Godot实用代码1000例_第15张图片

基于Tilemap的基础地图编辑

Godot实用代码1000例_第16张图片
为了简便起见,我们使用了如下的素材,作为TileMap的Tile来源。
Godot实用代码1000例_第17张图片

extends Node2D

onready var tilemap= $TileMap

func _input(event )
	if event is InputEventMouseButton and event.pressed:
		if event.button index =BUTTON LEFT:
			var pos = get_global_mouse_position()# 获取鼠标当前位置
			# 换算鼠标位置为TileMap中的格子坐标
			var x = floor(pos.x/tilemap.cell_size.x)
			var y = floor(pos.y/tilemap.cell_size.y)
			tilemap.set_cell(X,y,0) # 设定格子的图块为Tileset索引0的图块

基于Tilemap的基础地图编辑(多图块编辑)

AtlasTexture的引入

我们使用的素材还是跟之前一样。不过这里我们用到了AtlasTexture资源来处理。
Godot实用代码1000例_第18张图片
这要归功于Godoter群(笔者自己建立的一个Godot爱好者讨论群)中群友铁木球兄的推荐。
Godot实用代码1000例_第19张图片

extends Node2D

onready var tilemap = $TileMap
onready var btns = $CanvasLayer/Panel/Btns

var current_id = 0 # 当前图块的ID

func _input(event):
	if event is InputEventMouseButton and event.pressed:
		if event.button_index == BUTTON_LEFT:
			var pos = get_global_mouse_position() # 获取鼠标当前位置
			# 换算鼠标位置为TileMap中的格子坐标
			var x = floor(pos.x/tilemap.cell_size.x)
			var y = floor(pos.y/tilemap.cell_size.y)
			
			tilemap.set_cell(x,y,current_id) # 设定格子的图块为Tileset索引0的图块

func _ready():
	var sets:TileSet = tilemap.tile_set #获取tilemap的Tileset资源
	var sets_ids = sets.get_tiles_ids() # 获取Tileset中所有Tile的ID组成的数组
	for set_id in sets_ids: # 遍历数组
		var btn = TextureButton.new()
		var texture = AtlasTexture.new() # AtlasTexture将纹理的一部分裁剪出来
		texture.atlas = sets.tile_get_texture(set_id) # AtlasTexture.atlas是整体的图
		texture.region = sets.tile_get_region(set_id) # AtlasTexture.region是要显示的区域
		
		btn.texture_normal = texture
		btn.expand = true
		btn.stretch_mode = 0
		btn.rect_min_size = Vector2.ONE * 40
		btn.connect("pressed",self,"btn_pressed",[set_id])
		btns.add_child(btn)

func btn_pressed(set_id):
	current_id = set_id

Godot实用代码1000例_第20张图片
绿蓝红黄色块虽然也能表达意思,但是,为了更好的展现我们所做到的,可以把上面的素材改为下面这个:
Godot实用代码1000例_第21张图片
然后不用修改代码,你就可以看到自己创建的自定义编辑器编辑类似平台游戏地图的效果了。
Godot实用代码1000例_第22张图片

获取TileMap的TileSet,以及其中的Tile的ID

func ready()
	var sets:TileSet=tilemap.tile set#获取tilemap的Tileset资源
	var sets_ids=sets.get_tiles_ids()#获取Tileset中所有Tile的ID组成的数组
	for set_id in sets_ids:#逼历数组
		print(set id)#打印

Godot实用代码1000例_第23张图片

点击物体进行拖放

参考了Hi小胡《[Godot]实现物体的拖拽移动》视频

extends Area2D

var can_drag=false#是否开启拖动
var offset =Vector2.ZERO

#input_event信号只存在于CollisionObject及其子类
func on_Area2D_input_event(viewport,event,shape_idx):
#_Input是全局的,点哪都算,input_event:是只处理Area2D的CollisionShape范围内的
    if event is InputEventMouseButton:#这里的event与Input的event参数一致
        if event.button_index == BUTTON_LEFT:
            if event.pressed:
                can_drag = true
                offset.position=event.global_position
            else:
                can_drag=false


func _process(delta):
    if can_drag:
        position = get_global_mouse_position() + offset

视图操作

简单的鼠标滚轮缩放视图

参考了Hi小胡的《[G0dot]教你实现2D摄像机的视角控制(缩放,平移)》视频教程

extends Node2D

onready var cam = $Camera2D
var scale_factor:float = 1.0 # 当前的缩放比例值
var scale_step:float = 0.1 # 缩放步幅
var scale_max:float = 2.0 # 最大缩放倍数
var scale_min:float = 0.0 # 最小缩放倍数

func _ready():
    cam.position=get_viewport_rect().siZe/2 # 将摄像机与视口中心对齐

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == BUTTON_WHEEL_DOWN: # 鼠标滚轮向下
            if scale_factor<scale_max:
                scale_factor +scale_step
            elif event.button_index==BUTTON_WHEEL_UP: # 鼠标滚轮向上
                if scale_factor>scale_min:
                    scale_factor -scale_step
            # cam.Zoom=Vector:2.0NE*scale_factor # 设定相机的缩放倍数
func _process(delta):
    cam.zoom = lerp(cam.zoom,Vector2.0NE * scale_factor,8 * delta) # 用lerp让缩放更加丝滑

鼠标左键拖动视图

extends Node2D

onready var cam = $Camera2D

var can_drag =false
var start_pos:Vector2#记录初始的鼠标位置

func _ready():
    cam.position = get_viewport_rect().size/2#将摄像机与视口中心对齐

func _input(event):
#鼠标左键按下
    if event is InputEventMouseButton and event.pressed:
        if event.button_index == BUTTON_LEFT:
            can_drag=true#开启视图拖放
            start_pos=get_global_mouse_position()#记录拖放开始时鼠标的位置
    
    #视图拖放进行中且鼠标移动时
    if event is InputEventMouseMotion and can_drag:
        var end_pos=get_global_mouse_position()#记录当前鼠标位置
        cam.position-=end_pos-start_pos#摄像机位置-鼠标移动的距离
    
    #鼠标左键释放
    if event is InputEventMouseButton and !event.pressed:
        can_drag = false#关闭视图拖放

Ctrl+鼠标滚轮旋转视图

extends Node2D


onready var cam = $Camera2D
var rotate_step = 1.0 # 单次旋转度数
var ctrl = false # 是否按下Ctrl键


func _ready():
    cam.rotating=true#开启相机“可旋转”属性,不开启则摄像机无法进行旋转
    cam.position=get_viewport_rect().size/2#将摄像机与视口中心对齐



func _input(event):
    if event is InputEventKey:#键盘按键
        if event.pressed:
            if event.scancode == KEY_CONTROL: # Ctrl键
                ctrl = true
        else:
            ctrl = false
    
    if event is InputEventMouseButton and ctrl:
        if event.button_index == BUTTON_WHEEL_UP:
            cam.rotation_degrees += rotate_step
        elif event.button_index == BUTTON_WHEEL_DOWN:
            cam.rotation_degrees -= rotate_step

为了让效果看起来更丝滑,我们还是加入lerp:

extends Node2D


onready var cam = $Camera2D
var rotate_step = 10.0 # 单次旋转度数
var ctrl = false # 是否按下Ctrl键
var to_rotation_degrees:float

func _ready():
    cam.rotating=true#开启相机“可旋转”属性,不开启则摄像机无法进行旋转
    cam.position=get_viewport_rect().size/2#将摄像机与视口中心对齐



func _input(event):
    if event is InputEventKey:#键盘按键
        if event.pressed:
            if event.scancode == KEY_CONTROL: # Ctrl键
                ctrl = true
        else:
            ctrl = false
 
	if event is InputEventMouseButton and ctrl:
	    if event.button_index == BUTTON_WHEEL_UP:
	        to_rotation_degrees = cam.rotation_degrees + rotate_step
	    elif event.button_index == BUTTON_WHEEL_DOWN:
	        to_rotation_degrees = cam.rotation_degrees - rotate_step

func _process(delta):
    if ctrl:
        cam.rotation_degrees = lerp(cam.rotation_degrees,to_rotation_degrees,10*delta)

你可能感兴趣的:(Godot,godot,游戏引擎)