Start the game! Just adds a basic player character with rough movement
This commit is contained in:
commit
d640ad707f
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
BIN
Sprites/hockey_player.png
Normal file
BIN
Sprites/hockey_player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
34
Sprites/hockey_player.png.import
Normal file
34
Sprites/hockey_player.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bknjh6k2ocrri"
|
||||
path="res://.godot/imported/hockey_player.png-2943b0999ca2f3e81e7d84176d3012c9.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Sprites/hockey_player.png"
|
||||
dest_files=["res://.godot/imported/hockey_player.png-2943b0999ca2f3e81e7d84176d3012c9.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
BIN
Sprites/hockey_player.xcf
Normal file
BIN
Sprites/hockey_player.xcf
Normal file
Binary file not shown.
6
game.gd
Normal file
6
game.gd
Normal file
@ -0,0 +1,6 @@
|
||||
extends Node2D
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
$PlayerController.set_player_node($Player)
|
12
game.tscn
Normal file
12
game.tscn
Normal file
@ -0,0 +1,12 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://d2skh3wa6s2j7"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://b3llhcsie7y4u" path="res://player.tscn" id="1_2ej6p"]
|
||||
[ext_resource type="Script" path="res://game.gd" id="1_swmdi"]
|
||||
[ext_resource type="PackedScene" uid="uid://clntxmhd5uhlt" path="res://player_controller.tscn" id="3_v82a6"]
|
||||
|
||||
[node name="Game" type="Node2D"]
|
||||
script = ExtResource("1_swmdi")
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("1_2ej6p")]
|
||||
|
||||
[node name="PlayerController" parent="." instance=ExtResource("3_v82a6")]
|
1
icon.svg
Normal file
1
icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><rect x="2" y="2" width="124" height="124" rx="14" fill="#363d52" stroke="#212532" stroke-width="4"/><g transform="scale(.101) translate(122 122)"><g fill="#fff"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 813 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H447l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c3 34 55 34 58 0v-86c-3-34-55-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></g></svg>
|
After Width: | Height: | Size: 950 B |
37
icon.svg.import
Normal file
37
icon.svg.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bg5wwos0fgs03"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
78
player.gd
Normal file
78
player.gd
Normal file
@ -0,0 +1,78 @@
|
||||
extends Area2D
|
||||
|
||||
|
||||
|
||||
# Maximum radial turning speed of the player
|
||||
@export var turning_speed = PI/4 # (rad/sec)
|
||||
# Maximum speed added to player's velocity per push
|
||||
@export var skate_speed = 100 # (pixels/sec)
|
||||
# Maximum speed deceleration while stopping
|
||||
@export var braking_speed = 200 # (pixels/sec^2)
|
||||
# "Natural" deceleration
|
||||
@export var decel_ratio = 0.15 # (decel applied per second)
|
||||
|
||||
var velocity: Vector2
|
||||
|
||||
func _ready():
|
||||
velocity = Vector2.ZERO
|
||||
|
||||
|
||||
func _physics_process(delta: float):
|
||||
# Flip sprite
|
||||
if velocity.length() > 0:
|
||||
$AnimatedSprite2D.flip_h = velocity.x > 0
|
||||
|
||||
# Update position
|
||||
position = clamp_position(position + (velocity * delta))
|
||||
|
||||
# Decelerate naturally
|
||||
# TODO fix vector math, need to apply to hypotenuse, not the sides
|
||||
# TODO fix decel math, think compounding interest, it doesn't work this way
|
||||
# TODO brake harder when almost stopped
|
||||
velocity = velocity * (1 - decel_ratio * delta)
|
||||
|
||||
|
||||
func push(effort: float, angle: float):
|
||||
var delta_v = effort * skate_speed
|
||||
velocity += delta_v * Vector2.from_angle(angle)
|
||||
|
||||
|
||||
# TODO how to do doc comments?
|
||||
# TODO should applying delta come from the controller or the player? It doesn't
|
||||
# matter too much because these objects are pretty tightly coupled anyways.
|
||||
func turn(effort: float, delta: float):
|
||||
# Continuosly call with the effort while turning
|
||||
|
||||
# effort is +ve right, -ve left? maybe, some orientation
|
||||
velocity = velocity.rotated(effort * turning_speed * delta)
|
||||
|
||||
|
||||
func brake(effort: float, delta: float):
|
||||
var delta_v = effort * braking_speed * delta
|
||||
delta_v = min(delta_v, velocity.length())
|
||||
velocity -= delta_v * velocity.normalized()
|
||||
|
||||
|
||||
# TODO setup collision from world, and edges of arena, not the window
|
||||
func clamp_position(new_pos: Vector2) -> Vector2:
|
||||
var size = Vector2(get_window().size)
|
||||
if new_pos.x > size.x:
|
||||
if velocity.x > 0:
|
||||
velocity.x = 0
|
||||
new_pos.x = size.x
|
||||
if new_pos.x < 0:
|
||||
if velocity.x < 0:
|
||||
velocity.x = 0
|
||||
|
||||
new_pos.x = 0
|
||||
|
||||
if new_pos.y > size.y:
|
||||
if velocity.y > 0:
|
||||
velocity.y = 0
|
||||
new_pos.y = size.y
|
||||
if new_pos.y < 0:
|
||||
if velocity.y < 0:
|
||||
velocity.y = 0
|
||||
new_pos.y = 0
|
||||
|
||||
return new_pos
|
30
player.tscn
Normal file
30
player.tscn
Normal file
@ -0,0 +1,30 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://b3llhcsie7y4u"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bknjh6k2ocrri" path="res://Sprites/hockey_player.png" id="1_dtrap"]
|
||||
[ext_resource type="Script" path="res://player.gd" id="1_gh3p0"]
|
||||
|
||||
[sub_resource type="SpriteFrames" id="SpriteFrames_oslmh"]
|
||||
animations = [{
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": ExtResource("1_dtrap")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"default",
|
||||
"speed": 5.0
|
||||
}]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_exun0"]
|
||||
size = Vector2(42, 108)
|
||||
|
||||
[node name="Player" type="Area2D"]
|
||||
process_physics_priority = 1
|
||||
script = ExtResource("1_gh3p0")
|
||||
metadata/_edit_group_ = true
|
||||
|
||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
||||
sprite_frames = SubResource("SpriteFrames_oslmh")
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(-7, -6)
|
||||
shape = SubResource("RectangleShape2D_exun0")
|
85
player_controller.gd
Normal file
85
player_controller.gd
Normal file
@ -0,0 +1,85 @@
|
||||
extends Node
|
||||
|
||||
# Variables used by controller
|
||||
@export var skate_push_start_len = 0.2
|
||||
@export var skate_push_end_len = 0.9
|
||||
@export var skate_min_time = 0.02
|
||||
@export var skate_max_time = 0.1
|
||||
@export var skate_max_turn_radius = PI / 8
|
||||
@export var brake_deadzone = 0.3
|
||||
|
||||
var skate_occurred: bool
|
||||
var time_for_push: float
|
||||
|
||||
var player_node: Node = null
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
skate_occurred = false
|
||||
time_for_push = 0
|
||||
|
||||
|
||||
# Called every physics processing step
|
||||
func _physics_process(delta):
|
||||
if player_node == null:
|
||||
# Don't call the processing functions if the player node isn't set
|
||||
return
|
||||
|
||||
var joy = Input.get_vector("move_left", "move_right", "move_up", "move_down", 0)
|
||||
handle_skate_push(joy, delta)
|
||||
handle_turning(joy, delta)
|
||||
handle_braking(joy, delta)
|
||||
|
||||
|
||||
func set_player_node(node: Node):
|
||||
player_node = node
|
||||
|
||||
|
||||
func handle_skate_push(joy: Vector2, delta: float):
|
||||
var pos = joy.length()
|
||||
if pos >= skate_push_start_len and !skate_occurred:
|
||||
time_for_push += delta
|
||||
|
||||
if pos >= skate_push_end_len and !skate_occurred:
|
||||
if time_for_push >= skate_min_time and time_for_push <= skate_max_time:
|
||||
var effort = 1 - time_for_push / skate_max_time
|
||||
player_node.push(effort, joy.angle())
|
||||
|
||||
skate_occurred = true
|
||||
|
||||
elif pos < skate_push_start_len:
|
||||
skate_occurred = false
|
||||
time_for_push = 0
|
||||
|
||||
|
||||
func handle_turning(joy: Vector2, delta: float):
|
||||
if joy.length() < 0.95:
|
||||
# Only turn if the stick is at the edge
|
||||
return
|
||||
|
||||
# TODO this doesn't work well when you hit a wall
|
||||
var radial_diff = player_node.velocity.angle_to(joy)
|
||||
if (radial_diff >= PI/3):
|
||||
# Only turn for roughlyone "half" of the stick. The other half is used for braking
|
||||
return
|
||||
|
||||
var is_negative: bool = radial_diff < 0
|
||||
var turn_effort = min(abs(radial_diff), skate_max_turn_radius) / skate_max_turn_radius
|
||||
if is_negative:
|
||||
turn_effort = -turn_effort
|
||||
|
||||
player_node.turn(turn_effort, delta)
|
||||
|
||||
|
||||
func handle_braking(joy: Vector2, delta: float):
|
||||
if joy.length() < brake_deadzone:
|
||||
# Only brake past a certain amount
|
||||
return
|
||||
|
||||
var radial_diff = player_node.velocity.angle_to(joy)
|
||||
if (radial_diff < 3 * PI / 4):
|
||||
# Only brake if controller is mostly facing away from velocity
|
||||
return
|
||||
|
||||
player_node.brake(joy.length(), delta)
|
||||
|
6
player_controller.tscn
Normal file
6
player_controller.tscn
Normal file
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://clntxmhd5uhlt"]
|
||||
|
||||
[ext_resource type="Script" path="res://player_controller.gd" id="1_fj1gd"]
|
||||
|
||||
[node name="PlayerController" type="Node"]
|
||||
script = ExtResource("1_fj1gd")
|
39
project.godot
Normal file
39
project.godot
Normal file
@ -0,0 +1,39 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Offside"
|
||||
run/main_scene="res://game.tscn"
|
||||
config/features=PackedStringArray("4.1", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[input]
|
||||
|
||||
move_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
|
||||
]
|
||||
}
|
||||
move_up={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
|
||||
]
|
||||
}
|
||||
move_down={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null)
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user