Godot101(五) player-controlled Sprite
新遊戲——撿寶石
How to make the sprite be controled by player?
如何操縱遊戲主人公,以及如果處理碰撞事件
在node list中,搜索「collision」,對於2D部分,在「Node2D」下面能看到「CollisionObject2D」類以及下屬的若干children,特別是Area2D和3個Body2D。
3個Body2D是為了處理不同類型的碰撞以及物理模擬,每一個都有優缺點,不同的遊戲選用適當的Body2D。
- KinematicBody能讓sprite移動,icon就是一個入侵的人,也經常被稱為arcade physics。它能夠製造一面牆(可能不顯示),使得人物不能通過
- RigidBody被用來製造真實的人物,例如憤怒的小鳥,具有重力引擎的
- StaticBody是一個不會動的物體,用來製造牆、地面等不需要計算它們的運動情況
Area2D基本上能告訴你,當被衝擊到後,區域會有重疊到。
先建一個Area2D來感應是否受到碰撞,命名為player。再建一個child node,類型為sprite,拖一張人像給sprite。node2D
選中player後點擊如下,可以固定住player,使得拖動其下的sprite後,不會發生sprite與player的脫離。
但現在還不能感應。給player添加一個child node,類型為CollisionShape2D,建完後會看到一個警告:必須提供一個shape.
在CollisionShape2D的inspector區域有很多shape,選個最簡單的矩形,人物附近會出現一塊藍色區域,這就是碰撞區
並顯示內部只有兩個可以拖動的點(外部的框千萬不要動!)
這兩個點是對稱的,要完全覆蓋人物,就變成
這是因為藍色區域的中點需要調節一下,目前它的位置是(0,0)
而sprite的大小是(128x256),上部有一些空白,圖片上面的空白是物理化的、沒辦法更改。
我們追蹤的中心是root node的中心,這裡是player的pos,即下圖紅框的中心,這個中心目前沒有落在sprite的中心上:
但可以將sprite相對Area2D的位置向上調整一下,-64即可,然後就可以看到Area2D的中點和sprite的中點接近了。
現在給player添加指令。在其上新建GDsript,我們想通過按鍵控制player,按鍵的對應關係在project setting ->imput map中能看到。這裡還可以創建新的鍵對應,例如用一個鍵控制射擊等。
`````````````````
extends Area2D
export var SPEED=400
var vel=Vector2(1,1)
func _ready():
set_physics_process(true)
set_position(Vector2(500,300))
func _physics_process(delta):
var input = Vector2(0,0)
input.x=int(Input.is_action_just_pressed("ui_right"))-int(Input.is_action_just_pressed("ui_left")) # Input...返回邏輯值
input.y=int(Input.is_action_just_pressed("ui_down"))-int(Input.is_action_just_pressed("ui_up"))
vel = input.normalized()*SPEED
set_position(get_position()+vel*delta)
#如下也可以
#func _physics_process(delta):
#var pos = get_position()
#var input = Vector2(0,0)
#input.x=int(Input.is_action_just_pressed("ui_right"))- int(Input.is_action_just_pressed("ui_left"))
#input.y=int(Input.is_action_just_pressed("ui_down"))-int(Input.is_action_just_pressed("ui_up"))
#vel = input.normalized()*SPEED
#pos += vel*delta
#set_position(pos)
``````````````````````````````````````
這裡,
_physics_process is recommended for physics bodies, as is tied to the physics simulation clock. _process runs every frame, but is not guaranteed to have a consistent delta.
vel這裡定義為帶方向的速度,SPEED指明速度大小,input經過標準化後指明方向。vel = input.normalized()*SPEED;
改為方法Input.is_action_pressed()可以使得當按鍵不彈起時,持續執行。
然而,到達屏幕邊緣player還是凹陷進去了。下面借鑒上一講的彈球遊戲,用extent和screensize限定player的行動界限。
``````````````
extends Area2D
export var SPEED=400
var vel=Vector2(1,1)
var screensize
var extent
var pos
func _ready():
screensize=get_viewport_rect().size
set_position(screensize/2)
extent = $Sprite.get_texture().get_size()/2 # 等價於get_node("Spirite").get_texture().get_size()/2
set_physics_process(true)
func _physics_process(delta):
var input = Vector2(0,0)
input.x=int(Input.is_action_just_pressed("ui_right"))-int(Input.is_action_just_pressed("ui_left"))# Input...返回邏輯值
input.y=int(Input.is_action_just_pressed("ui_down"))-int(Input.is_action_just_pressed("ui_up"))
vel = input.normalized()*SPEED
pos += vel*delta
if pos.x>screensize.x-extent.x:
pos.x= screensize.x-extent.x
if pos.x<extent.x:
pos.x = extent.x
if pos.y>screensize.y-extent.y:
pos.y= screensize.y-extent.y
if pos.y<extent.y:
pos.y = extent.y
set_position(pos)
``````````````
其中最重要的是要記住,_ready里定義的變數是無法傳遞到_physics_process中的,因而需要將pos、screensize、extent這樣的變數聲明為全局的,通過局部函數更新;或者僅在局部函數中聲明並應用。例如也可以這樣寫:
``````````````
extends Area2D
export var SPEED=400
var vel=Vector2(1,1)
var screensize
func _ready():
screensize=get_viewport_rect().size
set_position(screensize/2)
set_physics_process(true)
func _physics_process(delta):
var pos = get_position()
var extent = $Sprite.get_texture().get_size()/2
var input = Vector2(0,0)
input.x=int(Input.is_action_just_pressed("ui_right"))-int(Input.is_action_just_pressed("ui_left"))
input.y=int(Input.is_action_just_pressed("ui_down"))-int(Input.is_action_just_pressed("ui_up"))
vel = input.normalized()*SPEED
pos += vel*delta
if pos.x>screensize.x-extent.x:
pos.x= screensize.x-extent.x
if pos.x<extent.x:
pos.x = extent.x
if pos.y>screensize.y-extent.y:
pos.y= screensize.y-extent.y
if pos.y<extent.y:
pos.y = extent.y
set_position(pos)
``````````````
還是很靈活的。
然而,由於這個圖片比較高,特別是還有空白的部分,所以當到達top和bottom時效果不好。需要藉助clamp()函數。
```````
extends Area2D
export var SPEED=400
var vel=Vector2(1,1)
var screensize
func _ready():
screensize=get_viewport_rect().size
set_position(screensize/2)
set_physics_process(true)
func _physics_process(delta):
var pos = get_position()
var extent = $Sprite.get_texture().get_size()/2
var input = Vector2(0,0)
input.x=int(Input.is_action_just_pressed("ui_right"))-int(Input.is_action_just_pressed("ui_left"))
input.y=int(Input.is_action_just_pressed("ui_down"))-int(Input.is_action_just_pressed("ui_up"))
vel = input.normalized()*SPEED
pos += vel*delta
pos.x = clamp(pos.x, 20, screensize.x-20)
pos.y = clamp(pos.y, 20, screensize.y-20)
set_position(pos)
```````
pos.x=clamp(pos.x, a, b)的意思是限制pos.x在a到b之間。若介於其間,則等號右邊返回pos.x;若超過邊界值,則等號右邊返回邊界值。這裡由於原圖不規則,自己調整了邊界。
推薦閱讀: