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;若超過邊界值,則等號右邊返回邊界值。這裡由於原圖不規則,自己調整了邊界。


推薦閱讀:

什麼是產品運營?
小程序時代來臨!你還在等候?
如何評價2017年滴滴的發展?
互聯網這大風吹的越狠,實業的心就越盪

TAG:互聯網 | 遊戲編程 | 軟體開發 |