Getting Started With Arcanepad on Godot 3
Welcome to the Arcanepad Godot Tutorial! This guide will help you get started with creating Arcanepad games in Godot.
Cool demo
Starter Template Tutorial
WARNING
Breaking changes were made to the way we use events, in the video I use strings to call events like "GetQuaternion", but now is AEventName.GetQuaternion instead, this is explained on the next video after this on the "Breaking changes to event names" section, and also has been updated on the template repo code, so if you read the code and follow how is structured you shouldn't have any problems.
Repo
https://github.com/imvenx/arcanepad-godot3-sdk
WARNING
Since there is no GDScript code syntax highlight support, we have to use PHP syntax highlight. So the highlight will not be totally accurate.
extends Node2D
const PadScenePath = "res://Scenes/Pad/Pad.tscn"
const ViewScenePath = "res://Scenes/View/View.tscn"
var dir = Directory.new()
func _ready():
call_deferred("goToPropperScene")
func goToPropperScene():
if dir.file_exists(PadScenePath) and not dir.file_exists(ViewScenePath):
goToPadScene()
return
goToViewScene()
func goToPadScene():
var _sceneChange = get_tree().change_scene(PadScenePath)
queue_free()
func goToViewScene():
var viewScene = load(ViewScenePath).instance()
get_tree().root.add_child(viewScene)
get_tree().current_scene = viewScene
queue_free()
extends Node2D
const PadScenePath = "res://Scenes/Pad/Pad.tscn"
const ViewScenePath = "res://Scenes/View/View.tscn"
var dir = Directory.new()
func _ready():
call_deferred("goToPropperScene")
func goToPropperScene():
if dir.file_exists(PadScenePath) and not dir.file_exists(ViewScenePath):
goToPadScene()
return
goToViewScene()
func goToPadScene():
var _sceneChange = get_tree().change_scene(PadScenePath)
queue_free()
func goToViewScene():
var viewScene = load(ViewScenePath).instance()
get_tree().root.add_child(viewScene)
get_tree().current_scene = viewScene
queue_free()
extends Node
var players := []
var gameStarted := false
var isGamePaused := false
var playerScene = preload("res://Scenes/View/Player/Player.tscn")
func _ready():
initView()
func initView():
Arcane.init()
# LISTEN WHEN THIS CLIENT (THE VIEW) IS INITIALIZED
Arcane.signals.connect(AEventName.ArcaneClientInitialized, self, "onArcaneClientInitialized")
# LISTEN WHEN ANY GAMEPAD CONNECTS
Arcane.signals.connect(AEventName.IframePadConnect, self, "onIframePadConnect")
# LISTEN WHEN ANY GAMEPAD DISCONNECTS
Arcane.signals.connect(AEventName.IframePadDisconnect, self, "onIframePadDisconnect")
# CREATE A PLAYER FOR EACH GAMEPAD THAT WAS CONNECTED BEFORE THE VIEW HAD INITIALIZED
func onArcaneClientInitialized(initialState):
for pad in initialState.pads:
createPlayer(pad)
# FOR EACH GAMEPAD THAT CONNECTS, CREATE A PLAYER IF DONT EXIST
func onIframePadConnect(e, _from):
var playerExists = false
for _player in players:
if _player.pad.iframeId == e.iframeId:
playerExists = true
break
if playerExists:
return
var pad = ArcanePad.new(e.deviceId, e.internalId, e.iframeId, true, e.user)
createPlayer(pad)
# DESTROY THE PLAYER ON GAMEPAD DISCONNECT (YOU CAN CHANGE THIS LOGIC, FOR EXAMPLE TO PAUSE OR WARN USERS)
func onIframePadDisconnect(e, _from):
var player = null
for _player in players:
if _player.pad.iframeId == e.iframeId:
player = _player
break
if player == null:
push_error("Player not found to remove on disconnect")
return
destroy_player(player)
func createPlayer(pad):
# This prevents creating the player if our pad app haven't been loaded
if pad.iframeId == null || pad.iframeId == "": return
var newPlayer = playerScene.instance()
print(newPlayer)
newPlayer.initialize(pad)
add_child(newPlayer)
players.append(newPlayer)
func destroy_player(player):
players.erase(player)
if player: player.queue_free()
extends Node
var players := []
var gameStarted := false
var isGamePaused := false
var playerScene = preload("res://Scenes/View/Player/Player.tscn")
func _ready():
initView()
func initView():
Arcane.init()
# LISTEN WHEN THIS CLIENT (THE VIEW) IS INITIALIZED
Arcane.signals.connect(AEventName.ArcaneClientInitialized, self, "onArcaneClientInitialized")
# LISTEN WHEN ANY GAMEPAD CONNECTS
Arcane.signals.connect(AEventName.IframePadConnect, self, "onIframePadConnect")
# LISTEN WHEN ANY GAMEPAD DISCONNECTS
Arcane.signals.connect(AEventName.IframePadDisconnect, self, "onIframePadDisconnect")
# CREATE A PLAYER FOR EACH GAMEPAD THAT WAS CONNECTED BEFORE THE VIEW HAD INITIALIZED
func onArcaneClientInitialized(initialState):
for pad in initialState.pads:
createPlayer(pad)
# FOR EACH GAMEPAD THAT CONNECTS, CREATE A PLAYER IF DONT EXIST
func onIframePadConnect(e, _from):
var playerExists = false
for _player in players:
if _player.pad.iframeId == e.iframeId:
playerExists = true
break
if playerExists:
return
var pad = ArcanePad.new(e.deviceId, e.internalId, e.iframeId, true, e.user)
createPlayer(pad)
# DESTROY THE PLAYER ON GAMEPAD DISCONNECT (YOU CAN CHANGE THIS LOGIC, FOR EXAMPLE TO PAUSE OR WARN USERS)
func onIframePadDisconnect(e, _from):
var player = null
for _player in players:
if _player.pad.iframeId == e.iframeId:
player = _player
break
if player == null:
push_error("Player not found to remove on disconnect")
return
destroy_player(player)
func createPlayer(pad):
# This prevents creating the player if our pad app haven't been loaded
if pad.iframeId == null || pad.iframeId == "": return
var newPlayer = playerScene.instance()
print(newPlayer)
newPlayer.initialize(pad)
add_child(newPlayer)
players.append(newPlayer)
func destroy_player(player):
players.erase(player)
if player: player.queue_free()
extends Node2D
func _ready():
initPad()
func initPad():
Arcane.init({'padOrientation': 'Portrait', 'deviceType': 'pad'})
# LISTEN WHEN THIS CLIENT (GAMEPAD) IS INITIALIZED
Arcane.signals.connect(AEventName.ArcaneClientInitialized, self, "onArcaneClientInitialized")
func onArcaneClientInitialized(initialState):
# LISTEN CUSTOM EVENT FROM THE VIEW
Arcane.signals.addSignal(EventName.TakeDamage)
Arcane.signals.connect(EventName.TakeDamage, self, "onTakeDamage")
func _on_CalibrateQuaternion_pressed():
Arcane.pad.calibrateQuaternion()
func _on_CalibratePointerTopLeft_pressed():
Arcane.pad.calibratePointer(true)
func _on_CalibratePointerBottomRight_pressed():
Arcane.pad.calibratePointer(false)
func _on_Attack_pressed():
# EMIT CUSTOM EVENT TO THE VIEWS
Arcane.msg.emitToViews(Events.AttackEvent.new())
func onTakeDamage(e, from):
Arcane.pad.vibrate(200)
Arcane.utils.writeToScreen("Taken " + str(e.damage) + " damage! Ouch!")
extends Node2D
func _ready():
initPad()
func initPad():
Arcane.init({'padOrientation': 'Portrait', 'deviceType': 'pad'})
# LISTEN WHEN THIS CLIENT (GAMEPAD) IS INITIALIZED
Arcane.signals.connect(AEventName.ArcaneClientInitialized, self, "onArcaneClientInitialized")
func onArcaneClientInitialized(initialState):
# LISTEN CUSTOM EVENT FROM THE VIEW
Arcane.signals.addSignal(EventName.TakeDamage)
Arcane.signals.connect(EventName.TakeDamage, self, "onTakeDamage")
func _on_CalibrateQuaternion_pressed():
Arcane.pad.calibrateQuaternion()
func _on_CalibratePointerTopLeft_pressed():
Arcane.pad.calibratePointer(true)
func _on_CalibratePointerBottomRight_pressed():
Arcane.pad.calibratePointer(false)
func _on_Attack_pressed():
# EMIT CUSTOM EVENT TO THE VIEWS
Arcane.msg.emitToViews(Events.AttackEvent.new())
func onTakeDamage(e, from):
Arcane.pad.vibrate(200)
Arcane.utils.writeToScreen("Taken " + str(e.damage) + " damage! Ouch!")
extends Node
var pad:ArcanePad
var padQuaternion = Quat()
onready var meshChild = get_child(0)
onready var pointer = get_child(2)
func initialize(_pad:ArcanePad) -> void:
pad = _pad
prints("Pad user", pad.user.name, "initialized")
# LISTEN WHEN THIS GAMEPAD CONNECTS
pad.connect(AEventName.IframePadConnect, self, 'onIframePadConnect')
# LISTEN WHEN THIS GAMEPAD DISCONNECTS
pad.connect(AEventName.IframePadDisconnect, self, 'onIframePadDisconnect')
# ASK FOR DEVICE ROTATION AND POINTER
pad.startGetQuaternion()
pad.startGetPointer()
# LISTEN FOR DEVICE ROTATION AND POINTER
pad.connect(AEventName.GetQuaternion, self, 'onGetQuaternion')
pad.connect(AEventName.GetPointer, self, 'onGetPointer')
# LISTEN CUSTOM EVENT FROM PAD
pad.addSignal(EventName.Attack)
pad.connect(EventName.Attack, self, 'Attack')
func _process(_delta):
meshChild.transform.basis = Basis(padQuaternion)
func _exit_tree():
pad.queue_free()
func onIframePadConnect(_e):
pass
func onIframePadDisconnect(_e):
pass
func onGetQuaternion(q):
if(q.w == null || q.x == null || q.y == null || q.z == null): return
padQuaternion.x = -q.x
padQuaternion.y = -q.y
padQuaternion.z = q.z
padQuaternion.w = q.w
func onGetPointer(e):
if(e.x == null || e.y == null): return
var viewport_size = get_viewport().get_size()
var new_x = viewport_size.x * (e.x / 100.0)
var new_y = viewport_size.y * (e.y / 100.0)
pointer.position = Vector2(new_x, new_y)
func Attack(e):
Arcane.utils.writeToScreen(pad.user.name + " Attacked")
print(pad.user.name + " Attacked!")
print(e)
# EMIT CUSTOM EVENT TO THE PAD
pad.emit(Events.TakeDamageEvent.new(3))
extends Node
var pad:ArcanePad
var padQuaternion = Quat()
onready var meshChild = get_child(0)
onready var pointer = get_child(2)
func initialize(_pad:ArcanePad) -> void:
pad = _pad
prints("Pad user", pad.user.name, "initialized")
# LISTEN WHEN THIS GAMEPAD CONNECTS
pad.connect(AEventName.IframePadConnect, self, 'onIframePadConnect')
# LISTEN WHEN THIS GAMEPAD DISCONNECTS
pad.connect(AEventName.IframePadDisconnect, self, 'onIframePadDisconnect')
# ASK FOR DEVICE ROTATION AND POINTER
pad.startGetQuaternion()
pad.startGetPointer()
# LISTEN FOR DEVICE ROTATION AND POINTER
pad.connect(AEventName.GetQuaternion, self, 'onGetQuaternion')
pad.connect(AEventName.GetPointer, self, 'onGetPointer')
# LISTEN CUSTOM EVENT FROM PAD
pad.addSignal(EventName.Attack)
pad.connect(EventName.Attack, self, 'Attack')
func _process(_delta):
meshChild.transform.basis = Basis(padQuaternion)
func _exit_tree():
pad.queue_free()
func onIframePadConnect(_e):
pass
func onIframePadDisconnect(_e):
pass
func onGetQuaternion(q):
if(q.w == null || q.x == null || q.y == null || q.z == null): return
padQuaternion.x = -q.x
padQuaternion.y = -q.y
padQuaternion.z = q.z
padQuaternion.w = q.w
func onGetPointer(e):
if(e.x == null || e.y == null): return
var viewport_size = get_viewport().get_size()
var new_x = viewport_size.x * (e.x / 100.0)
var new_y = viewport_size.y * (e.y / 100.0)
pointer.position = Vector2(new_x, new_y)
func Attack(e):
Arcane.utils.writeToScreen(pad.user.name + " Attacked")
print(pad.user.name + " Attacked!")
print(e)
# EMIT CUSTOM EVENT TO THE PAD
pad.emit(Events.TakeDamageEvent.new(3))
# YOUR CUSTOM EVENTS
class_name Events
class AttackEvent extends AEvents.ArcaneBaseEvent:
func _init().(EventName.Attack):
pass
class TakeDamageEvent extends AEvents.ArcaneBaseEvent:
var damage: int
func _init(_damage: int).(EventName.TakeDamage):
damage = _damage
# YOUR CUSTOM EVENTS
class_name Events
class AttackEvent extends AEvents.ArcaneBaseEvent:
func _init().(EventName.Attack):
pass
class TakeDamageEvent extends AEvents.ArcaneBaseEvent:
var damage: int
func _init(_damage: int).(EventName.TakeDamage):
damage = _damage
# YOUR CUSTOM EVENT NAMES
class_name EventName
const Attack = "Attack"
const TakeDamage = "TakeDamage"
# YOUR CUSTOM EVENT NAMES
class_name EventName
const Attack = "Attack"
const TakeDamage = "TakeDamage"
Breaking changes to event names
Upload your game to Arcanepad
Go to https://dev.arcanepad.com, create an account and after you are verified you can upload your game. The app folder has to be compressed on .zip format.