├── icon.png ├── textures ├── line.png ├── grid-blue.png ├── grid-dragon.png ├── grid-purple.png ├── grid-purple.png.import ├── line.png.import ├── grid-blue.png.import └── grid-dragon.png.import ├── default_env.tres ├── .gitignore ├── CustomRigidBody.gd ├── LineRenderer ├── LineRenderer.tscn └── LineRenderer.gd ├── PhysGun.tscn ├── icon.png.import ├── player.tscn ├── player.gd ├── TestMap.tscn ├── project.godot └── PhysGun.gd /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIREXE/godot-physgun/HEAD/icon.png -------------------------------------------------------------------------------- /textures/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIREXE/godot-physgun/HEAD/textures/line.png -------------------------------------------------------------------------------- /textures/grid-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIREXE/godot-physgun/HEAD/textures/grid-blue.png -------------------------------------------------------------------------------- /textures/grid-dragon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIREXE/godot-physgun/HEAD/textures/grid-dragon.png -------------------------------------------------------------------------------- /textures/grid-purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIREXE/godot-physgun/HEAD/textures/grid-purple.png -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot-specific ignores 2 | .import/ 3 | export.cfg 4 | export_presets.cfg 5 | 6 | # Mono-specific ignores 7 | .mono/ 8 | 9 | # System/tool-specific ignores 10 | .directory 11 | *~ 12 | 13 | -------------------------------------------------------------------------------- /CustomRigidBody.gd: -------------------------------------------------------------------------------- 1 | extends RigidBody 2 | 3 | class_name CustomRigidBody 4 | 5 | signal on_integrate_forces 6 | 7 | func _integrate_forces(state): 8 | emit_signal("on_integrate_forces", state, self) 9 | 10 | -------------------------------------------------------------------------------- /LineRenderer/LineRenderer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://LineRenderer/LineRenderer.gd" type="Script" id=1] 4 | 5 | [node name="LineRenderer" type="ImmediateGeometry"] 6 | script = ExtResource( 1 ) 7 | points = [ Vector3( 0, 0, 0 ), Vector3( 0, 4, 0 ) ] 8 | scaleTexture = false 9 | -------------------------------------------------------------------------------- /PhysGun.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://LineRenderer/LineRenderer.gd" type="Script" id=1] 4 | [ext_resource path="res://textures/line.png" type="Texture" id=2] 5 | [ext_resource path="res://PhysGun.gd" type="Script" id=3] 6 | 7 | [sub_resource type="SpatialMaterial" id=1] 8 | flags_transparent = true 9 | flags_unshaded = true 10 | albedo_texture = ExtResource( 2 ) 11 | 12 | [node name="PhysGun" type="Spatial"] 13 | script = ExtResource( 3 ) 14 | 15 | [node name="LineRenderer" type="ImmediateGeometry" parent="."] 16 | material_override = SubResource( 1 ) 17 | script = ExtResource( 1 ) 18 | -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /textures/grid-purple.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/grid-purple.png-78239ea508163509a3247f414cafaa23.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://textures/grid-purple.png" 13 | dest_files=[ "res://.import/grid-purple.png-78239ea508163509a3247f414cafaa23.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /player.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://player.gd" type="Script" id=1] 4 | [ext_resource path="res://PhysGun.tscn" type="PackedScene" id=2] 5 | 6 | [sub_resource type="CapsuleShape" id=1] 7 | radius = 0.644263 8 | 9 | [node name="Player" type="KinematicBody"] 10 | script = ExtResource( 1 ) 11 | 12 | [node name="CollisionShape" type="CollisionShape" parent="."] 13 | transform = Transform( 1, 0, 0, 0, -4.37114e-008, -1, 0, 1, -4.37114e-008, 0, 1.10033, 0 ) 14 | shape = SubResource( 1 ) 15 | 16 | [node name="Rotation_Helper" type="Spatial" parent="."] 17 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0 ) 18 | 19 | [node name="Camera" type="Camera" parent="Rotation_Helper"] 20 | 21 | [node name="PhysGun" parent="." instance=ExtResource( 2 )] 22 | 23 | [node name="LaserStartPosition" type="Spatial" parent="PhysGun"] 24 | transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0.743268, 1.42839, -0.181888 ) 25 | 26 | [editable path="PhysGun"] 27 | -------------------------------------------------------------------------------- /textures/line.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path.s3tc="res://.import/line.png-2f462dda442215fb8a9145c49fb514bf.s3tc.stex" 6 | path.etc2="res://.import/line.png-2f462dda442215fb8a9145c49fb514bf.etc2.stex" 7 | metadata={ 8 | "imported_formats": [ "s3tc", "etc2" ], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://textures/line.png" 15 | dest_files=[ "res://.import/line.png-2f462dda442215fb8a9145c49fb514bf.s3tc.stex", "res://.import/line.png-2f462dda442215fb8a9145c49fb514bf.etc2.stex" ] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/lossy_quality=0.7 21 | compress/hdr_mode=0 22 | compress/bptc_ldr=0 23 | compress/normal_map=0 24 | flags/repeat=true 25 | flags/filter=true 26 | flags/mipmaps=true 27 | flags/anisotropic=false 28 | flags/srgb=1 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/HDR_as_SRGB=false 32 | process/invert_color=false 33 | stream=false 34 | size_limit=0 35 | detect_3d=false 36 | svg/scale=1.0 37 | -------------------------------------------------------------------------------- /textures/grid-blue.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path.s3tc="res://.import/grid-blue.png-1cafe07555beef7afd8cb29ff1e041a5.s3tc.stex" 6 | path.etc2="res://.import/grid-blue.png-1cafe07555beef7afd8cb29ff1e041a5.etc2.stex" 7 | metadata={ 8 | "imported_formats": [ "s3tc", "etc2" ], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://textures/grid-blue.png" 15 | dest_files=[ "res://.import/grid-blue.png-1cafe07555beef7afd8cb29ff1e041a5.s3tc.stex", "res://.import/grid-blue.png-1cafe07555beef7afd8cb29ff1e041a5.etc2.stex" ] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/lossy_quality=0.7 21 | compress/hdr_mode=0 22 | compress/bptc_ldr=0 23 | compress/normal_map=0 24 | flags/repeat=true 25 | flags/filter=true 26 | flags/mipmaps=true 27 | flags/anisotropic=true 28 | flags/srgb=1 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/HDR_as_SRGB=false 32 | process/invert_color=false 33 | stream=false 34 | size_limit=0 35 | detect_3d=false 36 | svg/scale=1.0 37 | -------------------------------------------------------------------------------- /textures/grid-dragon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path.s3tc="res://.import/grid-dragon.png-59dfb3fcbdfaed15a22ea0af243acab8.s3tc.stex" 6 | path.etc2="res://.import/grid-dragon.png-59dfb3fcbdfaed15a22ea0af243acab8.etc2.stex" 7 | metadata={ 8 | "imported_formats": [ "s3tc", "etc2" ], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://textures/grid-dragon.png" 15 | dest_files=[ "res://.import/grid-dragon.png-59dfb3fcbdfaed15a22ea0af243acab8.s3tc.stex", "res://.import/grid-dragon.png-59dfb3fcbdfaed15a22ea0af243acab8.etc2.stex" ] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/lossy_quality=0.7 21 | compress/hdr_mode=0 22 | compress/bptc_ldr=0 23 | compress/normal_map=0 24 | flags/repeat=true 25 | flags/filter=true 26 | flags/mipmaps=true 27 | flags/anisotropic=true 28 | flags/srgb=1 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/HDR_as_SRGB=false 32 | process/invert_color=false 33 | stream=false 34 | size_limit=0 35 | detect_3d=false 36 | svg/scale=1.0 37 | -------------------------------------------------------------------------------- /player.gd: -------------------------------------------------------------------------------- 1 | extends KinematicBody 2 | 3 | const GRAVITY = -24.8 4 | var vel = Vector3() 5 | const MAX_SPEED = 10 6 | const JUMP_SPEED = 10 7 | const ACCEL = 4.5 8 | 9 | var dir = Vector3() 10 | 11 | const DEACCEL= 16 12 | const MAX_SLOPE_ANGLE = 40 13 | 14 | var camera 15 | var rotation_helper 16 | 17 | var MOUSE_SENSITIVITY = 0.05 18 | 19 | func _ready(): 20 | camera = $Rotation_Helper/Camera 21 | rotation_helper = $Rotation_Helper 22 | 23 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) 24 | 25 | func _physics_process(delta): 26 | process_input(delta) 27 | process_movement(delta) 28 | 29 | func process_input(delta): 30 | 31 | # ---------------------------------- 32 | # Walking 33 | dir = Vector3() 34 | var cam_xform = camera.get_global_transform() 35 | 36 | var input_movement_vector = Vector2() 37 | 38 | if Input.is_action_pressed("movement_forward"): 39 | input_movement_vector.y += 1 40 | if Input.is_action_pressed("movement_backward"): 41 | input_movement_vector.y -= 1 42 | if Input.is_action_pressed("movement_left"): 43 | input_movement_vector.x -= 1 44 | if Input.is_action_pressed("movement_right"): 45 | input_movement_vector.x += 1 46 | 47 | input_movement_vector = input_movement_vector.normalized() 48 | 49 | # Basis vectors are already normalized. 50 | dir += -cam_xform.basis.z * input_movement_vector.y 51 | dir += cam_xform.basis.x * input_movement_vector.x 52 | # ---------------------------------- 53 | 54 | # ---------------------------------- 55 | # Jumping 56 | if is_on_floor(): 57 | if Input.is_action_just_pressed("movement_jump"): 58 | vel.y = JUMP_SPEED 59 | # ---------------------------------- 60 | 61 | # ---------------------------------- 62 | # Capturing/Freeing the cursor 63 | if Input.is_action_just_pressed("ui_cancel"): 64 | if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE: 65 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) 66 | else: 67 | Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) 68 | # ---------------------------------- 69 | 70 | func process_movement(delta): 71 | dir.y = 0 72 | dir = dir.normalized() 73 | 74 | vel.y += delta * GRAVITY 75 | 76 | var hvel = vel 77 | hvel.y = 0 78 | 79 | var target = dir 80 | target *= MAX_SPEED 81 | 82 | var accel 83 | if dir.dot(hvel) > 0: 84 | accel = ACCEL 85 | else: 86 | accel = DEACCEL 87 | hvel = hvel.linear_interpolate(target, accel * delta) 88 | vel.x = hvel.x 89 | vel.z = hvel.z 90 | vel = move_and_slide(vel, Vector3(0, 1, 0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE)) 91 | 92 | func _input(event): 93 | if not Input.is_action_pressed("phys_rotate"): 94 | if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: 95 | rotation_helper.rotate_x(deg2rad(-event.relative.y * MOUSE_SENSITIVITY)) 96 | self.rotate_y(deg2rad(event.relative.x * MOUSE_SENSITIVITY * -1)) 97 | 98 | var camera_rot = rotation_helper.rotation_degrees 99 | camera_rot.x = clamp(camera_rot.x, -70, 70) 100 | rotation_helper.rotation_degrees = camera_rot 101 | -------------------------------------------------------------------------------- /TestMap.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=2] 2 | 3 | [ext_resource path="res://textures/grid-dragon.png" type="Texture" id=1] 4 | [ext_resource path="res://textures/grid-blue.png" type="Texture" id=2] 5 | [ext_resource path="res://player.tscn" type="PackedScene" id=3] 6 | 7 | [sub_resource type="SpatialMaterial" id=2] 8 | albedo_texture = ExtResource( 2 ) 9 | uv1_triplanar = true 10 | 11 | [sub_resource type="SpatialMaterial" id=1] 12 | albedo_texture = ExtResource( 1 ) 13 | uv1_scale = Vector3( -1, 1, 1 ) 14 | 15 | [sub_resource type="BoxShape" id=3] 16 | 17 | [sub_resource type="BoxShape" id=4] 18 | extents = Vector3( 0.5, 0.5, 0.5 ) 19 | 20 | [node name="Spatial" type="Spatial"] 21 | 22 | [node name="CSGBox2" type="CSGBox" parent="."] 23 | transform = Transform( 0.999999, 0.000756822, 0.000788284, -0.000756055, 0.999999, -0.000951271, -0.00078902, 0.000950661, 0.999999, 0, -2.10725, 0 ) 24 | use_collision = true 25 | width = 20.2545 26 | depth = 22.3369 27 | material = SubResource( 2 ) 28 | 29 | [node name="DirectionalLight" type="DirectionalLight" parent="."] 30 | transform = Transform( 0.447104, -0.561507, 0.696281, 0, 0.778418, 0.627746, -0.894482, -0.280667, 0.348034, 0, 0, 0 ) 31 | shadow_enabled = true 32 | 33 | [node name="Player" parent="." instance=ExtResource( 3 )] 34 | transform = Transform( 0.0278868, 0, 0.999611, 0, 1, 0, -0.999611, 0, 0.0278868, 4.68499, -0.466529, 0 ) 35 | 36 | [node name="RigidBody" type="RigidBody" parent="."] 37 | transform = Transform( 0.999999, 0.000756822, 0.000788284, -0.000756055, 0.999999, -0.000951271, -0.00078902, 0.000950661, 0.999999, 0, -0.10554, 0 ) 38 | mass = 10.0 39 | 40 | [node name="CSGBox" type="CSGBox" parent="RigidBody"] 41 | transform = Transform( 1, -6.969e-011, -5.82077e-011, 7.90124e-011, 1, 0, 0, 5.82077e-011, 1, 0, 0, 0 ) 42 | material = SubResource( 1 ) 43 | 44 | [node name="CollisionShape" type="CollisionShape" parent="RigidBody"] 45 | transform = Transform( 1, -6.969e-011, -5.82077e-011, 7.90124e-011, 1, 0, 0, 5.82077e-011, 1, 0, 0, 0 ) 46 | shape = SubResource( 3 ) 47 | 48 | [node name="RigidBody2" type="RigidBody" parent="."] 49 | transform = Transform( 0.576349, -0.000341609, -0.817203, -0.000756055, 0.999999, -0.000951271, 0.817203, 0.00116612, 0.576349, 0.687618, -0.10554, -2.55785 ) 50 | mass = 10.0 51 | 52 | [node name="CSGBox" type="CSGBox" parent="RigidBody2"] 53 | transform = Transform( 1, -6.969e-011, -5.82077e-011, 7.90124e-011, 1, 0, 0, 5.82077e-011, 1, 0, 0, 0 ) 54 | material = SubResource( 1 ) 55 | 56 | [node name="CollisionShape" type="CollisionShape" parent="RigidBody2"] 57 | transform = Transform( 1, -6.969e-011, -5.82077e-011, 7.90124e-011, 1, 0, 0, 5.82077e-011, 1, 0, 0, 0 ) 58 | shape = SubResource( 3 ) 59 | 60 | [node name="RigidBody4" type="RigidBody" parent="."] 61 | transform = Transform( 0.979786, 0.000550491, -0.200049, -0.000756055, 0.999999, -0.000951271, 0.200048, 0.00108328, 0.979785, 1.86039, -0.60771, -0.73243 ) 62 | mass = 10.0 63 | 64 | [node name="CSGBox" type="CSGBox" parent="RigidBody4"] 65 | transform = Transform( 1, -6.969e-011, -5.82077e-011, 7.90124e-011, 1, 0, 0, 5.82077e-011, 1, 0, 0, 0 ) 66 | width = 1.0 67 | height = 1.0 68 | depth = 1.0 69 | material = SubResource( 1 ) 70 | 71 | [node name="CollisionShape" type="CollisionShape" parent="RigidBody4"] 72 | transform = Transform( 1, -1.16415e-010, -8.9407e-008, 1.16415e-010, 1, 0, 5.96046e-008, 1.16415e-010, 1, 0, 0, 0 ) 73 | shape = SubResource( 4 ) 74 | 75 | [node name="RigidBody3" type="RigidBody" parent="."] 76 | transform = Transform( 0.999999, 0.000756822, 0.000788284, -0.000756055, 0.999999, -0.000951271, -0.00078902, 0.000950661, 0.999999, 0, 2.05445, -1.21538 ) 77 | mass = 10.0 78 | 79 | [node name="CSGBox" type="CSGBox" parent="RigidBody3"] 80 | transform = Transform( 1, -1.28182e-010, -2.32831e-010, 1.95257e-010, 1, 0, 0, 1.74623e-010, 1, 0, 0, 0 ) 81 | material = SubResource( 1 ) 82 | 83 | [node name="CollisionShape" type="CollisionShape" parent="RigidBody3"] 84 | transform = Transform( 1, -6.969e-011, -5.82077e-011, 7.90124e-011, 1, 0, 0, 5.82077e-011, 1, 0, 0, 0 ) 85 | shape = SubResource( 3 ) 86 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ { 12 | "base": "RigidBody", 13 | "class": "CustomRigidBody", 14 | "language": "GDScript", 15 | "path": "res://CustomRigidBody.gd" 16 | }, { 17 | "base": "ImmediateGeometry", 18 | "class": "LineRenderer", 19 | "language": "GDScript", 20 | "path": "res://LineRenderer/LineRenderer.gd" 21 | } ] 22 | _global_script_class_icons={ 23 | "CustomRigidBody": "", 24 | "LineRenderer": "" 25 | } 26 | 27 | [application] 28 | 29 | config/name="PhysGun" 30 | run/main_scene="res://TestMap.tscn" 31 | config/icon="res://icon.png" 32 | 33 | [input] 34 | 35 | movement_jump={ 36 | "deadzone": 0.5, 37 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"unicode":0,"echo":false,"script":null) 38 | ] 39 | } 40 | movement_forward={ 41 | "deadzone": 0.5, 42 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null) 43 | ] 44 | } 45 | movement_backward={ 46 | "deadzone": 0.5, 47 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null) 48 | ] 49 | } 50 | movement_left={ 51 | "deadzone": 0.5, 52 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null) 53 | ] 54 | } 55 | movement_right={ 56 | "deadzone": 0.49, 57 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null) 58 | ] 59 | } 60 | fire1={ 61 | "deadzone": 0.5, 62 | "events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null) 63 | ] 64 | } 65 | phys_back={ 66 | "deadzone": 0.5, 67 | "events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":5,"pressed":false,"doubleclick":false,"script":null) 68 | ] 69 | } 70 | phys_forward={ 71 | "deadzone": 0.5, 72 | "events": [ Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":4,"pressed":false,"doubleclick":false,"script":null) 73 | ] 74 | } 75 | phys_rotate={ 76 | "deadzone": 0.5, 77 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":82,"unicode":0,"echo":false,"script":null) 78 | ] 79 | } 80 | rotate_z={ 81 | "deadzone": 0.5, 82 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"unicode":0,"echo":false,"script":null) 83 | ] 84 | } 85 | 86 | [rendering] 87 | 88 | environment/default_environment="res://default_env.tres" 89 | -------------------------------------------------------------------------------- /LineRenderer/LineRenderer.gd: -------------------------------------------------------------------------------- 1 | extends ImmediateGeometry 2 | 3 | class_name LineRenderer 4 | 5 | export var points = [Vector3(0,0,0),Vector3(0,5,0)] 6 | export var startThickness = 0.1 7 | export var endThickness = 0.1 8 | export var cornerSmooth = 5 9 | export var capSmooth = 5 10 | export var drawCaps = true 11 | export var drawCorners = true 12 | export var globalCoords = true 13 | export var scaleTexture = true 14 | 15 | var camera 16 | var cameraOrigin 17 | 18 | func _ready(): 19 | pass 20 | 21 | func _process(delta): 22 | if points.size() < 2: 23 | return 24 | 25 | camera = get_viewport().get_camera() 26 | if camera == null: 27 | return 28 | cameraOrigin = to_local(camera.get_global_transform().origin) 29 | 30 | var progressStep = 1.0 / points.size(); 31 | var progress = 0; 32 | var thickness = lerp(startThickness, endThickness, progress); 33 | var nextThickness = lerp(startThickness, endThickness, progress + progressStep); 34 | 35 | clear() 36 | begin(Mesh.PRIMITIVE_TRIANGLES) 37 | 38 | for i in range(points.size() - 1): 39 | var A = points[i] 40 | var B = points[i+1] 41 | 42 | if globalCoords: 43 | A = to_local(A) 44 | B = to_local(B) 45 | 46 | var AB = B - A; 47 | var orthogonalABStart = (cameraOrigin - ((A + B) / 2)).cross(AB).normalized() * thickness; 48 | var orthogonalABEnd = (cameraOrigin - ((A + B) / 2)).cross(AB).normalized() * nextThickness; 49 | 50 | var AtoABStart = A + orthogonalABStart 51 | var AfromABStart = A - orthogonalABStart 52 | var BtoABEnd = B + orthogonalABEnd 53 | var BfromABEnd = B - orthogonalABEnd 54 | 55 | if i == 0: 56 | if drawCaps: 57 | cap(A, B, thickness, capSmooth) 58 | 59 | if scaleTexture: 60 | var ABLen = AB.length() 61 | var ABFloor = floor(ABLen) 62 | var ABFrac = ABLen - ABFloor 63 | 64 | set_uv(Vector2(ABFloor, 0)) 65 | add_vertex(AtoABStart) 66 | set_uv(Vector2(-ABFrac, 0)) 67 | add_vertex(BtoABEnd) 68 | set_uv(Vector2(ABFloor, 1)) 69 | add_vertex(AfromABStart) 70 | set_uv(Vector2(-ABFrac, 0)) 71 | add_vertex(BtoABEnd) 72 | set_uv(Vector2(-ABFrac, 1)) 73 | add_vertex(BfromABEnd) 74 | set_uv(Vector2(ABFloor, 1)) 75 | add_vertex(AfromABStart) 76 | else: 77 | set_uv(Vector2(1, 0)) 78 | add_vertex(AtoABStart) 79 | set_uv(Vector2(0, 0)) 80 | add_vertex(BtoABEnd) 81 | set_uv(Vector2(1, 1)) 82 | add_vertex(AfromABStart) 83 | set_uv(Vector2(0, 0)) 84 | add_vertex(BtoABEnd) 85 | set_uv(Vector2(0, 1)) 86 | add_vertex(BfromABEnd) 87 | set_uv(Vector2(1, 1)) 88 | add_vertex(AfromABStart) 89 | 90 | if i == points.size() - 2: 91 | if drawCaps: 92 | cap(B, A, nextThickness, capSmooth) 93 | else: 94 | if drawCorners: 95 | var C = points[i+2] 96 | if globalCoords: 97 | C = to_local(C) 98 | 99 | var BC = C - B; 100 | var orthogonalBCStart = (cameraOrigin - ((B + C) / 2)).cross(BC).normalized() * nextThickness; 101 | 102 | var angleDot = AB.dot(orthogonalBCStart) 103 | 104 | if angleDot > 0: 105 | corner(B, BtoABEnd, B + orthogonalBCStart, cornerSmooth) 106 | else: 107 | corner(B, B - orthogonalBCStart, BfromABEnd, cornerSmooth) 108 | 109 | progress += progressStep; 110 | thickness = lerp(startThickness, endThickness, progress); 111 | nextThickness = lerp(startThickness, endThickness, progress + progressStep); 112 | 113 | end() 114 | 115 | func cap(center, pivot, thickness, smoothing): 116 | var orthogonal = (cameraOrigin - center).cross(center - pivot).normalized() * thickness; 117 | var axis = (center - cameraOrigin).normalized(); 118 | 119 | var array = [] 120 | for i in range(smoothing + 1): 121 | array.append(Vector3(0,0,0)) 122 | array[0] = center + orthogonal; 123 | array[smoothing] = center - orthogonal; 124 | 125 | for i in range(1, smoothing): 126 | array[i] = center + (orthogonal.rotated(axis, lerp(0, PI, float(i) / smoothing))); 127 | 128 | for i in range(1, smoothing + 1): 129 | set_uv(Vector2(0, (i - 1) / smoothing)) 130 | add_vertex(array[i - 1]); 131 | set_uv(Vector2(0, (i - 1) / smoothing)) 132 | add_vertex(array[i]); 133 | set_uv(Vector2(0.5, 0.5)) 134 | add_vertex(center); 135 | 136 | func corner(center, start, end, smoothing): 137 | var array = [] 138 | for i in range(smoothing + 1): 139 | array.append(Vector3(0,0,0)) 140 | array[0] = start; 141 | array[smoothing] = end; 142 | 143 | var axis = start.cross(end) 144 | var offset = start - center 145 | var angle = offset.angle_to(end - center) 146 | for i in range(1, smoothing): 147 | if not is_equal_approx(axis.length_squared(), 0.0): 148 | array[i] = center + offset.rotated(axis.normalized(), lerp(0, angle, float(i) / smoothing)) 149 | else: 150 | array[i] = center 151 | 152 | for i in range(1, smoothing + 1): 153 | set_uv(Vector2(0, (i - 1) / smoothing)) 154 | add_vertex(array[i - 1]); 155 | set_uv(Vector2(0, (i - 1) / smoothing)) 156 | add_vertex(array[i]); 157 | set_uv(Vector2(0.5, 0.5)) 158 | add_vertex(center); 159 | 160 | -------------------------------------------------------------------------------- /PhysGun.gd: -------------------------------------------------------------------------------- 1 | extends Spatial 2 | 3 | var physics_moving_object : RigidBody 4 | var _pmo_local_hit_diff : Vector3 5 | var _pmo_rotation_diff : Quat 6 | var _pmo_distance : float 7 | 8 | const ROTATION_TOLERANCE = 0.8 9 | const ARC_RESOLUTION = 12 10 | 11 | onready var line_renderer : LineRenderer = get_node("LineRenderer") 12 | const ROTATION_SENSITIVITY = 0.0025 13 | 14 | const GRAB_MAX = 10.0 15 | const GRAB_MIN = 1.5 16 | 17 | var _scrollwheel_input : float = 0.0 18 | var _rotation_input := Vector3() 19 | var uv_animation_rate := Vector3(1.0, 0.0, 0.0) 20 | 21 | func _ready(): 22 | var line_mat := line_renderer.material_override 23 | line_mat.albedo_color = Color(0.0, 0.7, 1.0, 0.8) 24 | line_mat.uv1_scale = Vector3(5.0, 1.0, 1.0) 25 | line_renderer.points = [] 26 | 27 | for line in range(ARC_RESOLUTION): 28 | line_renderer.points.append(Vector3()) 29 | line_renderer.hide() 30 | 31 | func update_arc_points(a: Vector3, b: Vector3 , c: Vector3): 32 | b = lerp(a,b, 0.6) 33 | for i in range(line_renderer.points.size()): 34 | var t = float(i)/float(line_renderer.points.size()) 35 | 36 | line_renderer.points[i] = lerp(lerp(a, b ,t), lerp(b, c, t), t) 37 | 38 | line_renderer.points[line_renderer.points.size()-1] = c 39 | 40 | func _input(event): 41 | if event.is_action("phys_back"): 42 | _scrollwheel_input -= 1.0 43 | elif event.is_action("phys_forward"): 44 | _scrollwheel_input += 1.0 45 | 46 | if event is InputEventMouseMotion and Input.is_action_pressed("phys_rotate"): 47 | 48 | if abs(event.relative.y) > ROTATION_TOLERANCE: 49 | _rotation_input.y = event.relative.y * ROTATION_SENSITIVITY 50 | if abs(event.relative.x) > ROTATION_TOLERANCE: 51 | if Input.is_action_pressed("rotate_z"): 52 | _rotation_input.x = -event.relative.x * ROTATION_SENSITIVITY 53 | _rotation_input.y = 0 54 | else: 55 | _rotation_input.z = event.relative.x * ROTATION_SENSITIVITY 56 | if event.is_action("fire1"): 57 | fire(event) 58 | 59 | func fire(event: InputEvent): 60 | if event.is_pressed(): 61 | var screen_middle = get_viewport().size / 2 62 | var camera = get_viewport().get_camera() 63 | var from = camera.project_ray_origin(screen_middle) 64 | var to = from + camera.project_ray_normal(screen_middle) * GRAB_MAX 65 | var space_state = get_world().direct_space_state 66 | var result : Dictionary = space_state.intersect_ray(from, to, [self, get_parent()]) 67 | 68 | if result.has("collider"): 69 | 70 | if result.collider is RigidBody: 71 | var collider := result.collider as RigidBody 72 | if collider.mode == RigidBody.MODE_RIGID: 73 | physics_moving_object = collider 74 | _pmo_rotation_diff = Quat(camera.get_camera_transform().basis).inverse() * Quat(physics_moving_object.global_transform.basis) 75 | _pmo_local_hit_diff = physics_moving_object.to_local(result.position) 76 | 77 | _pmo_distance = (camera.get_camera_transform().origin - result.position).length() 78 | 79 | line_renderer.show() 80 | if not event.is_pressed() and physics_moving_object: 81 | 82 | physics_moving_object = null 83 | line_renderer.hide() 84 | 85 | func _physics_process(delta): 86 | 87 | if physics_moving_object: 88 | var camera = get_viewport().get_camera() 89 | var forward = camera.get_camera_transform().basis.z 90 | var right = camera.get_camera_transform().basis.x 91 | var up = camera.get_camera_transform().basis.y 92 | 93 | var relative_to_camera_rotation = Quat(camera.get_camera_transform().basis) * _pmo_rotation_diff 94 | 95 | var desired_rotation = relative_to_camera_rotation 96 | 97 | desired_rotation = Quat(forward, _rotation_input.x) * Quat(up, _rotation_input.z) * Quat(right, _rotation_input.y) * relative_to_camera_rotation 98 | 99 | 100 | _pmo_rotation_diff = Quat(camera.get_camera_transform().basis).inverse() * desired_rotation 101 | 102 | var screen_middle = get_viewport().size / 2 103 | var from = camera.project_ray_origin(screen_middle) 104 | var scroll_wheel_input = (5.0 * delta) * _scrollwheel_input 105 | var hold_point = from + camera.project_ray_normal(screen_middle) * clamp((_pmo_distance + scroll_wheel_input), GRAB_MIN, GRAB_MAX) 106 | var center_destination = hold_point - (physics_moving_object.global_transform.xform(_pmo_local_hit_diff) - physics_moving_object.global_transform.origin) 107 | var to_destination = center_destination - physics_moving_object.global_transform.origin 108 | var force = to_destination / delta * 0.6 / physics_moving_object.mass 109 | 110 | 111 | physics_moving_object.linear_velocity = Vector3() 112 | physics_moving_object.angular_velocity = Vector3() 113 | 114 | var rot_diff = desired_rotation * Quat(physics_moving_object.global_transform.basis).inverse() 115 | 116 | physics_moving_object.linear_velocity = force 117 | 118 | physics_moving_object.angular_velocity = rot_diff.get_euler() * 0.5 / delta 119 | # Distance to object recalculated 120 | 121 | _pmo_distance = (from - hold_point).length() 122 | 123 | # Laser beam variables 124 | 125 | var laser_start_point = $LaserStartPosition.global_transform.origin 126 | var laser_mid_point = hold_point 127 | var laser_end_point = physics_moving_object.global_transform.xform(_pmo_local_hit_diff) 128 | 129 | update_arc_points(laser_start_point, laser_mid_point, laser_end_point) 130 | 131 | # Laser animation 132 | 133 | line_renderer.material_override.uv1_offset += uv_animation_rate * delta 134 | 135 | # Input passby reset 136 | 137 | _scrollwheel_input = 0.0 138 | _rotation_input = Vector3() 139 | --------------------------------------------------------------------------------