save code

This commit is contained in:
xsl
2025-11-28 18:10:23 +08:00
commit fee064c3f0
49 changed files with 64353 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
+2
View File
@@ -0,0 +1,2 @@
# Godot 4+ specific ignores
.godot/
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+15
View File
@@ -0,0 +1,15 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: celyk
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+214
View File
@@ -0,0 +1,214 @@
@tool
@icon("bounce.svg")
class_name GPUTrail3D extends GPUParticles3D
## [br]A node for creating a ribbon trail effect.
## [br][color=purple]Made by celyk[/color]
##
## This node serves as an alternative to CPU based trails.[br]
# TODO:
# Add flipbook support
# Hide the actual GPUParticles3D node
# Restructure code, use enums for flags
# Add more polygons, make trail smoother
# Add an acceleration parameter
# Port to Godot 3.5
# Port to 2D
# Allow custom material
# PUBLIC
## Length is the number of steps in the trail
@export var length : int = 100 : set = _set_length
@export var length_seconds : float : set = _set_length
@export_category("Color / Texture")
## The main texture of the trail.[br]
## [br]Set [member vertical_texture] to adjust for orientation[br]
##
## [br]Enable [member use_red_as_alpha] to use the red color channel as alpha
@export var texture : Texture : set = _set_texture
## Scolls the texture by applying an offset to the UV
@export var scroll : Vector2 : set = _set_scroll
## A color ramp for modulating the color along the length of the trail
@export var color_ramp : GradientTexture1D : set = _set_color_ramp
## A curve for modulating the width along the length of the trail
@export var curve : CurveTexture : set = _set_curve
## Set [member vertical_texture] to adjust for orientation
@export var vertical_texture := false : set = _set_vertical_texture
## Enable [member use_red_as_alpha] to use the red color channel of [member texture] as alpha
@export var use_red_as_alpha := false : set = _set_use_red_as_alpha
@export_category("Mesh tweaks")
## Makes trail face camera. I haven't finished this yet
@export var billboard := false : set = _set_billboard
## Enable to improve the mapping of [member texture] to the trail
@export var dewiggle := true : set = _set_dewiggle
## Enable to improve the mapping of [member texture] to the trail
@export var clip_overlaps := true : set = _set_clip_overlaps
## Enable [member snap_to_transform] to snap the start of the trail to the nodes position. This may not be noticeable unless you
## have changed [member fixed_fps], which you can use to optimize the trail
@export var snap_to_transform := false : set = _set_snap_to_transform
# PRIVATE
const _DEFAULT_TEXTURE = "defaults/texture.tres"
const _DEFAULT_CURVE = "defaults/curve.tres"
var _defaults_have_been_set = false
func _get_property_list():
return [{"name": "_defaults_have_been_set","type": TYPE_BOOL,"usage": PROPERTY_USAGE_NO_EDITOR}]
func _ready():
if not _defaults_have_been_set:
_defaults_have_been_set = true
amount = length
lifetime = length
explosiveness = 1 # emits all particles at once
# the main fps is default
fixed_fps = int( DisplayServer.screen_get_refresh_rate(DisplayServer.MAIN_WINDOW_ID) )
if DisplayServer.screen_get_refresh_rate(DisplayServer.MAIN_WINDOW_ID) < 0.0:
push_warning("Could not find screen refresh rate. Using fixed_fps = 60")
fixed_fps = 60
process_material = ShaderMaterial.new()
process_material.shader = preload("shaders/trail.gdshader")
draw_pass_1 = QuadMesh.new()
draw_pass_1.material = ShaderMaterial.new()
draw_pass_1.material.shader = preload("shaders/trail_draw_pass.gdshader")
color_ramp = preload(_DEFAULT_TEXTURE).duplicate(true)
curve = preload(_DEFAULT_CURVE).duplicate(true)
draw_pass_1.material.resource_local_to_scene = true
length = length
vertical_texture = vertical_texture
use_red_as_alpha = use_red_as_alpha
billboard = billboard
dewiggle = dewiggle
clip_overlaps = clip_overlaps
snap_to_transform = snap_to_transform
func _set_length(value):
if value is int: # length is being set
length = value
length = max(length, 1)
length_seconds = float(length) / get_fixed_fps()
elif value is float: # length_seconds is being set
length = int(value * get_fixed_fps())
length = max(length, 1)
length_seconds = float(length) / get_fixed_fps()
if _defaults_have_been_set:
amount = length
lifetime = length
restart()
func _set_texture(value):
texture = value
_uv_offset = Vector2(0,0) # Reset the scroll when a new texture is assigned
if value:
draw_pass_1.material.set_shader_parameter("tex", texture)
else:
draw_pass_1.material.set_shader_parameter("tex", preload(_DEFAULT_TEXTURE))
func _set_scroll(value):
scroll = value
func _set_color_ramp(value):
color_ramp = value
draw_pass_1.material.set_shader_parameter("color_ramp", color_ramp)
func _set_curve(value):
curve = value
if value:
draw_pass_1.material.set_shader_parameter("curve", curve)
else:
draw_pass_1.material.set_shader_parameter("curve", preload(_DEFAULT_CURVE))
func _set_vertical_texture(value):
vertical_texture = value
_flags = _set_flag(_flags,0,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_use_red_as_alpha(value):
use_red_as_alpha = value
_flags = _set_flag(_flags,1,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_billboard(value):
billboard = value
_flags = _set_flag(_flags,2,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
if value && _defaults_have_been_set:
_update_billboard_transform( global_transform.basis[0] )
restart()
func _set_dewiggle(value):
dewiggle = value
_flags = _set_flag(_flags,3,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_snap_to_transform(value):
snap_to_transform = value
_flags = _set_flag(_flags,4,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
func _set_clip_overlaps(value):
clip_overlaps = value
_flags = _set_flag(_flags,5,value)
draw_pass_1.material.set_shader_parameter("flags", _flags)
@onready var _old_pos : Vector3 = global_position
@onready var _billboard_transform : Transform3D = global_transform
var _uv_offset : Vector2
func _process(delta):
if(snap_to_transform):
draw_pass_1.material.set_shader_parameter("emmission_transform", global_transform)
# Handle UV scrolling
_uv_offset += scroll * delta
_uv_offset = _uv_offset.posmod(1.0)
draw_pass_1.material.set_shader_parameter("uv_offset", _uv_offset)
await RenderingServer.frame_pre_draw
if(billboard):
var delta_position = global_position - _old_pos
if delta_position:
var tangent = global_transform.basis[1].length() * (delta_position).normalized()
_update_billboard_transform(tangent)
RenderingServer.instance_set_transform(get_instance(), _billboard_transform)
_old_pos = global_position
func _update_billboard_transform(tangent : Vector3):
_billboard_transform = global_transform
var p : Vector3 = _billboard_transform.basis[1]
var x : Vector3 = tangent
var angle : float = p.angle_to(x)
var rotation_axis : Vector3 = p.cross(x).normalized()
if rotation_axis != Vector3():
_billboard_transform.basis = _billboard_transform.basis.rotated(rotation_axis,angle)
_billboard_transform.basis = _billboard_transform.basis.scaled(Vector3(0.5,0.5,0.5))
_billboard_transform.origin += _billboard_transform.basis[1]
var _flags = 0
func _set_flag(i, idx : int, value : bool):
return (i & ~(1 << idx)) | (int(value) << idx)
+1
View File
@@ -0,0 +1 @@
uid://cx8ohpp5vs5os
+15
View File
@@ -0,0 +1,15 @@
# Godot 4+ specific ignores
.godot/
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 celyk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+70
View File
@@ -0,0 +1,70 @@
# GPUTrail
GPUTrail is a GPU-based trail plugin for Godot 4, offering an efficient alternative to CPU-based trails for creating ribbon trail effects in your games and applications.
![heart](https://github.com/celyk/GPUTrail/assets/50609684/a190fee3-682b-42b9-9bef-cd49a5e3b99c)
## Features
- GPU-accelerated trail rendering for improved performance
- Customizable trail length, texture, and color
- Support for color ramps and width curves
- Options for texture orientation and alpha channel handling
- Billboard mode for camera-facing trails
- Dewiggle and clip overlaps features for improved trail appearance
## Installation
1. Clone or download this repository
2. Copy the `addons/GPUTrail` folder into your Godot project's `addons` folder
3. Enable the plugin in your project settings: Project -> Project Settings -> Plugins -> GPUTrail
## Usage
1. Add a new `GPUTrail3D` node to your scene
2. Customize the trail properties in the Inspector panel
3. Attach the `GPUTrail3D` node to the object you want to trail
## Properties
- `length`: Number of steps in the trail
- `texture`: Main texture of the trail
- `color_ramp`: Color gradient along the trail's length
- `curve`: Width modulation along the trail's length
- `vertical_texture`: Adjust texture orientation
- `use_red_as_alpha`: Use the red channel of the texture as alpha
- `billboard`: Make the trail face the camera (experimental)
- `dewiggle`: Improve texture mapping to the trail
- `clip_overlaps`: Prevent trail self-intersectionis
- `snap_to_transform`: Snap the trail start to the node's position, and regardless of the particles own framerate
## Example
```gdscript
var trail = GPUTrail3D.new()
trail.length = 100
trail.texture = preload("res://trail_texture.png")
add_child(trail)
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Acknowledgements
- Tutorial by Le Lu: [YouTube Video](https://www.youtube.com/watch?v=0VsEfP4XFCM)
## TODO
- Add flipbook support
- Hide the actual GPUParticles3D node
- Restructure code, use enums for flags
- Add more polygons, make trail smoother
- Add an acceleration parameter
- Port to Godot 3.5
- Port to 2D
- Allow custom material
+1
View File
@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.699 12.31"><title>Bounce_ForExport</title><path d="M11.028,5A5.5,5.5,0,0,0,5.912,8.827c-.16-.259-.343-.658-.616-1.074,0,.04,0,.08,0,.121-.023.314-.064.627-.111.94-.021.3-.034.61-.051.916A5.02,5.02,0,0,1,4.9,11.223c.059.727.087,1.486.109,2.245-.006.226-.027.451-.016,1.124h2.22c.095-1.627.091-4.022,2.63-5.909a1.71,1.71,0,0,1-.079-.727A3.09,3.09,0,0,1,11.028,5Z" transform="translate(-0.091 -2.282)" style="fill:#ee478b"/><path d="M12.115,4.921a2.041,2.041,0,1,1-1.443.6,2.024,2.024,0,0,1,1.443-.6m0-1.23a3.271,3.271,0,1,0,2.312.958,3.26,3.26,0,0,0-2.312-.958Z" transform="translate(-0.091 -2.282)" style="fill:#ee478b;stroke:#ee478b;stroke-miterlimit:10;stroke-width:0.5px"/><path d="M14.87,4.251l-.086-.091c-.324-.34-1.29-1.574-1.29-1.574q-.058.057-.114.123l-.231.281L12.8,2.9a5.633,5.633,0,0,0-.787-.123,3.131,3.131,0,0,1-1.6-.5,1.463,1.463,0,0,0-.129.418l-.042.451-.448.061A7.231,7.231,0,0,0,5.819,5.068a1.664,1.664,0,0,1,1.364.49,2.151,2.151,0,0,1,.275.354A6.139,6.139,0,0,0,5.9,8.16a10.051,10.051,0,0,0-.727-.9A5.076,5.076,0,0,0,3.82,6.246a2.8,2.8,0,0,0-1.652-.273,2.479,2.479,0,0,0-1.441.765,2.414,2.414,0,0,1,1.457-.625,2.594,2.594,0,0,1,1.5.382,4.728,4.728,0,0,1,1.169,1.05,10.417,10.417,0,0,1,.809,1.232,11.688,11.688,0,0,0-.346,1.264,17.747,17.747,0,0,0-.326,4.551,17.567,17.567,0,0,1,.691-4.458A7.155,7.155,0,0,1,7.7,6.712a1.963,1.963,0,0,1-.067.385A2.746,2.746,0,0,1,6.443,8.56l.325.031a3.134,3.134,0,0,1,2.119.91,3.072,3.072,0,0,0-.725.378,3.07,3.07,0,0,1,2,.793,1.253,1.253,0,0,1,.413,1.039,4.356,4.356,0,0,0,.852-.39,6.453,6.453,0,0,1,1.076-.5l.03-.01.031-.006A4.069,4.069,0,0,0,14.87,4.251Zm-.583,4.883a3.072,3.072,0,1,1,0-4.345A3.072,3.072,0,0,1,14.287,9.134Z" transform="translate(-0.091 -2.282)" style="fill:#ffcc2c"/><path d="M.8,6.667c-.01.01-.025.016-.036.027s-.016.021-.026.031C.76,6.7.784,6.687.8,6.667Z" transform="translate(-0.091 -2.282)" style="fill:#ee478b"/><path d="M5.319,10.041a11.688,11.688,0,0,1,.346-1.264,10.417,10.417,0,0,0-.809-1.232A4.728,4.728,0,0,0,3.687,6.5a2.594,2.594,0,0,0-1.5-.382,2.413,2.413,0,0,0-1.448.618c-.854.873-1.077,2.33.691,3.262L.744,11.24c.116.043,1.109-.451,1.6.035.16.186.156.714-.354,1.189.382-.294.725-1.08.463-1.328s-.361-.31-1.108-.293a9.978,9.978,0,0,1,1.3-1.817C.485,10.472.591,5.812,3.277,7.105c1.448.945,1.631,3.318,1.712,5.757A16.51,16.51,0,0,1,5.319,10.041Z" transform="translate(-0.091 -2.282)" style="fill:#ee478b"/><path d="M6.82,11.769a8.9,8.9,0,0,1-.811-2.556c-.123.3-.232.609-.325.921a17.567,17.567,0,0,0-.691,4.458l1.035-1.073c.032-.568.759-.53.881-1.231C6.692,12.013,6.981,12.092,6.82,11.769Z" transform="translate(-0.091 -2.282)" style="fill:#ee478b"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+37
View File
@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bm2sig4cnve21"
path="res://.godot/imported/bounce.svg-1c60b713fc682b9c790d587bf0715690.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://GPUTrail-main/bounce.svg"
dest_files=["res://.godot/imported/bounce.svg-1c60b713fc682b9c790d587bf0715690.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
+9
View File
@@ -0,0 +1,9 @@
[gd_resource type="CurveTexture" load_steps=2 format=3 uid="uid://ct31fhxvcragr"]
[sub_resource type="Curve" id="Curve_7ufs5"]
bake_resolution = 16
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0.1), 0.0, 0.0, 0, 0]
point_count = 2
[resource]
curve = SubResource("Curve_7ufs5")
+7
View File
@@ -0,0 +1,7 @@
[gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://crk6pkb7e5rwc"]
[sub_resource type="Gradient" id="Gradient_nk704"]
colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0)
[resource]
gradient = SubResource("Gradient_nk704")
+27
View File
@@ -0,0 +1,27 @@
@tool
extends EditorNode3DGizmoPlugin
var editor_plugin : EditorPlugin
func _init(_editor_plugin:EditorPlugin):
editor_plugin = _editor_plugin
create_material("main", Color(1,1,1), false, true, true)
func _has_gizmo(node):
return node is GPUTrail3D
# show gizmo name in visibility list
func _get_gizmo_name():
return "GPUTrail3DGizmo"
func _redraw(gizmo):
gizmo.clear()
var node3d : Node3D = gizmo.get_node_3d()
var lines = PackedVector3Array()
lines.push_back(Vector3(0,1,0))
lines.push_back(Vector3(0,-1,0))
gizmo.add_lines(lines, get_material("main", gizmo), false, Color(1,1,1,1))
+1
View File
@@ -0,0 +1 @@
uid://k7rm0xjg8f61
+7
View File
@@ -0,0 +1,7 @@
[plugin]
name="GPUTrail"
description="temporary desc"
author="celyk"
version="0.1"
script="plugin.gd"
+19
View File
@@ -0,0 +1,19 @@
@tool
extends EditorPlugin
const MyCustomGizmoPlugin = preload("gizmos/gizmo.gd")
var gizmo_plugin = MyCustomGizmoPlugin.new(self)
func _enter_tree():
# Initialization of the plugin goes here.
# Add the new type with a name, a parent type, a script and an icon.
add_custom_type("GPUTrail3D", "GPUParticles3D", preload("GPUTrail3D.gd"), preload("bounce.svg"))
add_node_3d_gizmo_plugin(gizmo_plugin)
func _exit_tree():
# Clean-up of the plugin goes here.
# Always remember to remove it from the engine when deactivated.
remove_custom_type("GPUTrail3D")
remove_node_3d_gizmo_plugin(gizmo_plugin)
+1
View File
@@ -0,0 +1 @@
uid://wqdljanamd5c
@@ -0,0 +1,22 @@
shader_type spatial;
#define GPU_TRAIL
#include "res://addons/GPUTrail/shaders/gputrail_lib.gdshaderinc"
varying vec2 trail_uv;
void vertex(){
PREPARE_TRAIL();
// Save the trail UV coordaintes for later
trail_uv = UV;
// Scroll textures slowly along the length of the trail
//UV.x -= fract(TIME*.4);
}
void fragment(){
PREPARE_TRAIL_TEXTURES();
// Alpha erosion
ALPHA = max(ALPHA - trail_uv.x, 0.0);
}
@@ -0,0 +1 @@
uid://b0um4dniff5cs
@@ -0,0 +1,15 @@
shader_type spatial;
#define GPU_TRAIL
#include "res://addons/GPUTrail/shaders/gputrail_lib.gdshaderinc"
varying vec2 trail_uv;
void vertex(){
PREPARE_TRAIL();
}
void fragment(){
PREPARE_TRAIL_TEXTURES();
}
@@ -0,0 +1 @@
uid://df5unx54alf3g
@@ -0,0 +1,121 @@
#ifdef GPU_TRAIL
//#define GPU_TRAIL
// TODO make optional
render_mode unshaded,world_vertex_coords,cull_disabled;
uniform sampler2D tex : repeat_enable, source_color, hint_default_white;
uniform vec2 uv_offset = vec2(0);
uniform sampler2D color_ramp : repeat_disable, source_color, hint_default_white;
uniform sampler2D curve : repeat_disable, hint_default_white;
uniform mat4 emmission_transform = mat4(1);
uniform int flags = 0;
/*uniform bool vertical_texture = false;
uniform bool use_red_as_alpha = false;
uniform bool billboard = false;
uniform bool dewiggle = false;
uniform bool snap_to_transform = false;*/
#define vertical_texture bool(flags & 1)
#define use_red_as_alpha bool(flags & 2)
#define billboard bool(flags & 4)
#define dewiggle bool(flags & 8)
#define snap_to_transform bool(flags & 16)
#define clip_overlaps bool(flags & 32)
varying float scale_interp;
varying vec2 clip;
varying vec2 mesh_uv;
void missing_parens(){}
#define PREPARE_TRAIL { \
mesh_uv = UV; \
\
mat4 my_model_matrix = MODEL_MATRIX; \
if(snap_to_transform && INSTANCE_CUSTOM.w==2.0){ \
my_model_matrix[1] = emmission_transform * vec4(0,1,0,1); \
my_model_matrix[2] = emmission_transform * vec4(0,-1,0,1); \
} \
\
if(billboard){ \
vec3 t0 = my_model_matrix[0].xyz-my_model_matrix[3].xyz; \
vec3 t1 = my_model_matrix[1].xyz-my_model_matrix[2].xyz; \
\
vec3 up0 = length(t0)*normalize( \
cross( \
my_model_matrix[3].xyz-INV_VIEW_MATRIX[3].xyz, \
t0)); \
vec3 up1 = length(t1)*normalize( \
cross( \
my_model_matrix[2].xyz-INV_VIEW_MATRIX[3].xyz, \
t1)); \
\
my_model_matrix[0] = my_model_matrix[3]; \
my_model_matrix[1] = my_model_matrix[2]; \
\
my_model_matrix[0].xyz += up0; \
my_model_matrix[3].xyz -= up0; \
\
my_model_matrix[1].xyz += up1; \
my_model_matrix[2].xyz -= up1; \
} \
\
vec3 a = mix(my_model_matrix[1].xyz,my_model_matrix[0].xyz,UV.x); \
vec3 b = mix(my_model_matrix[2].xyz,my_model_matrix[3].xyz,UV.x); \
\
UV.x = (UV.x + INSTANCE_CUSTOM.w-1.0 - 2.0)/(INSTANCE_CUSTOM.z-1.0); \
\
\
float h = textureLod(curve, vec2(UV.x), 0.0).x; \
\
VERTEX = mix(a,b,(UV.y-0.5)*h + 0.5); \
\
if(dewiggle){ \
scale_interp = h; \
UV *= scale_interp; \
} \
\
\
clip.x = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[1].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[2].xyz - INV_VIEW_MATRIX[3].xyz)); \
clip.y = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[3].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[0].xyz - INV_VIEW_MATRIX[3].xyz)); \
} missing_parens
#define PREPARE_TRAIL_TEXTURES { \
vec2 clip0 = clip; \
float ababab = clip0.x*clip0.y; \
\
if(clip_overlaps && ababab < 0.0) { \
if(abs(mesh_uv.x-0.5)<0.5) \
discard; \
} \
\
vec2 base_uv = UV; \
\
if(dewiggle){ \
base_uv /= scale_interp; \
} \
\
vec2 raw_uv = base_uv; \
\
base_uv -= uv_offset; \
\
if(vertical_texture){ \
base_uv = base_uv.yx; \
} \
\
vec4 T = textureLod(tex, base_uv, 0.0); \
ALBEDO = T.rgb; \
ALPHA = T.a; \
\
if(use_red_as_alpha){ \
ALBEDO = vec3(1); \
ALPHA = T.r; \
} \
\
T = textureLod(color_ramp, raw_uv, 0.0); \
ALBEDO *= T.rgb; \
ALPHA *= T.a; \
} missing_parens
#endif /*GPU_TRAIL*/
@@ -0,0 +1 @@
uid://lxfndmjp47ua
+43
View File
@@ -0,0 +1,43 @@
shader_type particles;
render_mode keep_data,disable_force,disable_velocity;
void process() {
// CUSTOM.w tracks the particles place in the trail, in range (0..LIFETIME]
// requires that LIFETIME = number of particles
float amount = LIFETIME;
vec4 a = EMISSION_TRANSFORM * vec4(0,1,0,1);
vec4 b = EMISSION_TRANSFORM * vec4(0,-1,0,1);
// start
if(CUSTOM.w == 0.0){
CUSTOM.w = float(INDEX)+1.0;
// needed to pass to draw pass
CUSTOM.z = amount;
// needed to initialize in case of CUSTOM.w == 2.0
TRANSFORM = mat4(a,a,b,b);
}
// restart
if(CUSTOM.w == amount+1.0){
CUSTOM.w = 1.0;
}
if(CUSTOM.w == 1.0){
// sets the quad to the line to cache this frame, it is not yet visible
TRANSFORM = mat4(a,a,b,b);
}
if(CUSTOM.w == 2.0){
// sets the right edge of the quad
TRANSFORM[1] = a;
TRANSFORM[2] = b;
}
CUSTOM.w++;
}
+1
View File
@@ -0,0 +1 @@
uid://x3g5fson40t2
@@ -0,0 +1,130 @@
shader_type spatial;
render_mode unshaded,world_vertex_coords,cull_disabled;
uniform sampler2D tex : repeat_enable, source_color, hint_default_white;
uniform vec2 uv_offset = vec2(0);
uniform sampler2D color_ramp : repeat_disable, source_color, hint_default_white;
uniform sampler2D curve : repeat_disable, hint_default_white;
uniform mat4 emmission_transform = mat4(1);
uniform int flags = 0;
/*uniform bool vertical_texture = false;
uniform bool use_red_as_alpha = false;
uniform bool billboard = false;
uniform bool dewiggle = false;
uniform bool snap_to_transform = false;*/
#define vertical_texture bool(flags & 1)
#define use_red_as_alpha bool(flags & 2)
#define billboard bool(flags & 4)
#define dewiggle bool(flags & 8)
#define snap_to_transform bool(flags & 16)
#define clip_overlaps bool(flags & 32)
varying float scale_interp;
varying vec2 clip;
varying vec2 mesh_uv;
void vertex(){
mesh_uv = UV;
mat4 my_model_matrix = MODEL_MATRIX;
if(snap_to_transform && INSTANCE_CUSTOM.w==2.0){
my_model_matrix[1] = emmission_transform * vec4(0,1,0,1);
my_model_matrix[2] = emmission_transform * vec4(0,-1,0,1);
}
if(billboard){
vec3 t0 = my_model_matrix[0].xyz-my_model_matrix[3].xyz;
vec3 t1 = my_model_matrix[1].xyz-my_model_matrix[2].xyz;
//vec3 up1 = up0;
vec3 up0 = length(t0)*normalize(
cross(
my_model_matrix[3].xyz-INV_VIEW_MATRIX[3].xyz,
//-INV_VIEW_MATRIX[2].xyz,
t0));
vec3 up1 = length(t1)*normalize(
cross(
my_model_matrix[2].xyz-INV_VIEW_MATRIX[3].xyz,
//-INV_VIEW_MATRIX[2].xyz,
t1));
my_model_matrix[0] = my_model_matrix[3];
my_model_matrix[1] = my_model_matrix[2];
my_model_matrix[0].xyz += up0;
my_model_matrix[3].xyz -= up0;
my_model_matrix[1].xyz += up1;
my_model_matrix[2].xyz -= up1;
}
vec3 a = mix(my_model_matrix[1].xyz,my_model_matrix[0].xyz,UV.x);
vec3 b = mix(my_model_matrix[2].xyz,my_model_matrix[3].xyz,UV.x);
UV.x = (UV.x + INSTANCE_CUSTOM.w-1.0 - 2.0)/(INSTANCE_CUSTOM.z-1.0);
float h = textureLod(curve, vec2(UV.x), 0.0).x;//h=1.0;
VERTEX = mix(a,b,(UV.y-0.5)*h + 0.5);
if(dewiggle){
scale_interp = h;
UV *= scale_interp;
}
clip.x = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[1].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[2].xyz - INV_VIEW_MATRIX[3].xyz));
clip.y = dot(VERTEX - INV_VIEW_MATRIX[3].xyz,cross(my_model_matrix[3].xyz - INV_VIEW_MATRIX[3].xyz,my_model_matrix[0].xyz - INV_VIEW_MATRIX[3].xyz));
}
void fragment(){
//if(billboard && !FRONT_FACING) discard;
vec2 clip0 = clip;
float ababab = clip0.x*clip0.y;
//ababab += dFdx(ababab) + dFdy(ababab);
//clip0 -= fwidth(clip0);
if(clip_overlaps && ababab < 0.0) {
if(abs(mesh_uv.x-0.5)<0.5)
discard;
}
vec2 base_uv = UV;
if(dewiggle){
base_uv /= scale_interp;
}
vec2 raw_uv = base_uv;
// Handle UV scrolling
base_uv -= uv_offset;
if(vertical_texture){
base_uv = base_uv.yx;
}
vec4 T = textureLod(tex, base_uv, 0.0);
ALBEDO = T.rgb;
ALPHA = T.a;
if(use_red_as_alpha){
ALBEDO = vec3(1);
ALPHA = T.r;
}
T = textureLod(color_ramp, raw_uv, 0.0);
ALBEDO *= T.rgb;
ALPHA *= T.a;
//ALBEDO = vec3(UV,0);
if((base_uv.x < .01) || (.99 < base_uv.x)){
//ALBEDO = vec3(1,0,1);
}
}
@@ -0,0 +1 @@
uid://7xkxpgsmixhn
+2
View File
@@ -0,0 +1,2 @@
# Blender 3.6.0 MTL File: 'None'
# www.blender.org
+62867
View File
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
[remap]
importer="wavefront_obj"
importer_version=1
type="Mesh"
uid="uid://cmswy8js5ghvq"
path="res://.godot/imported/Mountains.obj-081ad0e719bf659be14b6580f50c05b4.mesh"
[deps]
files=["res://.godot/imported/Mountains.obj-081ad0e719bf659be14b6580f50c05b4.mesh"]
source_file="res://Models/Mountains.obj"
dest_files=["res://.godot/imported/Mountains.obj-081ad0e719bf659be14b6580f50c05b4.mesh", "res://.godot/imported/Mountains.obj-081ad0e719bf659be14b6580f50c05b4.mesh"]
[params]
generate_tangents=true
generate_lods=true
generate_shadow_mesh=true
generate_lightmap_uv2=false
generate_lightmap_uv2_texel_size=0.2
scale_mesh=Vector3(1, 1, 1)
offset_mesh=Vector3(0, 0, 0)
force_disable_mesh_compression=false
+86
View File
@@ -0,0 +1,86 @@
[gd_scene load_steps=11 format=3 uid="uid://cvwb0xnja8txj"]
[ext_resource type="Script" uid="uid://bkdqbagbitktv" path="res://Scripts/SpawnAoE.gd" id="1_uhfrq"]
[ext_resource type="Script" uid="uid://dq7h3nectp4hy" path="res://Scripts/camera.gd" id="3_ufqdh"]
[ext_resource type="ArrayMesh" uid="uid://cmswy8js5ghvq" path="res://Models/Mountains.obj" id="4_ulx4j"]
[sub_resource type="Resource" id="Resource_uhfrq"]
metadata/__load_path__ = "res://Prefabs/vfx_aoe_02.tscn"
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_338y4"]
sky_energy_multiplier = 0.65
[sub_resource type="Sky" id="Sky_s2m6d"]
sky_material = SubResource("ProceduralSkyMaterial_338y4")
[sub_resource type="Environment" id="Environment_01qi8"]
background_mode = 2
background_energy_multiplier = 0.38
sky = SubResource("Sky_s2m6d")
glow_enabled = true
glow_levels/6 = 0.13
glow_levels/7 = 0.25
glow_intensity = 0.1
glow_blend_mode = 0
glow_hdr_threshold = 0.5
glow_hdr_scale = 1.5
volumetric_fog_enabled = true
volumetric_fog_density = 0.0811
volumetric_fog_albedo = Color(0.243137, 0.243137, 0.243137, 1)
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_s4vrp"]
dof_blur_far_enabled = true
dof_blur_far_distance = 100.0
[sub_resource type="BoxMesh" id="BoxMesh_vf5wh"]
size = Vector3(150, 0, 150)
[sub_resource type="BoxShape3D" id="BoxShape3D_4gkui"]
size = Vector3(150, 1.93747, 150)
[node name="World" type="Node3D" node_paths=PackedStringArray("camera")]
script = ExtResource("1_uhfrq")
camera = NodePath("Scene/Camera3D")
aoe_prefab = SubResource("Resource_uhfrq")
[node name="Scene" type="Node" parent="."]
[node name="WorldEnvironment" type="WorldEnvironment" parent="Scene"]
environment = SubResource("Environment_01qi8")
camera_attributes = SubResource("CameraAttributesPractical_s4vrp")
[node name="Camera3D" type="Camera3D" parent="Scene/WorldEnvironment"]
transform = Transform3D(1, 0, 0, 0, 0.941294, 0.337588, 0, -0.337588, 0.941294, 0, 2.72096, 3.90772)
script = ExtResource("3_ufqdh")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 0.84934, 0.527846, 0, -0.527846, 0.84934, 0, 2.9349, 0)
light_energy = 0.5
[node name="Mountains" type="MeshInstance3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1.27348, 0, 0, 0, 1.26814, -21.5595, -3.87235, 0)
mesh = ExtResource("4_ulx4j")
skeleton = NodePath("../..")
[node name="Ground" type="StaticBody3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Scene/Ground"]
mesh = SubResource("BoxMesh_vf5wh")
skeleton = NodePath("../../..")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/Ground"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.468733, 0)
shape = SubResource("BoxShape3D_4gkui")
[node name="SpotLight3D" type="SpotLight3D" parent="Scene"]
transform = Transform3D(0.714169, 0.238575, -0.658061, 0.196049, 0.834321, 0.515241, 0.671958, -0.496982, 0.549074, 3.08513, 6.69863, 4.07749)
spot_range = 60.629
spot_attenuation = 0.67
spot_angle = 55.0
spot_angle_attenuation = 2.82843
[node name="Camera3D" type="Camera3D" parent="Scene"]
transform = Transform3D(0.827962, 0.25459, -0.499662, 0, 0.891007, 0.45399, 0.560784, -0.375887, 0.73772, -8.97652, 8.79728, 11.4697)
current = true
fov = 50.0
+116
View File
@@ -0,0 +1,116 @@
[gd_scene load_steps=14 format=3 uid="uid://jf6lhabdtxqs"]
[ext_resource type="Script" uid="uid://gs00msx4i83u" path="res://Scripts/SpawnProjectile.gd" id="1_idti0"]
[ext_resource type="Script" uid="uid://dq7h3nectp4hy" path="res://Scripts/camera.gd" id="3_ynfkw"]
[ext_resource type="ArrayMesh" uid="uid://cmswy8js5ghvq" path="res://Models/Mountains.obj" id="4_qjsfk"]
[ext_resource type="Script" uid="uid://b86hxdolpatb8" path="res://Scripts/RotateToMouse2.gd" id="5_5wsiv"]
[sub_resource type="Resource" id="Resource_h7mdb"]
metadata/__load_path__ = "res://Prefabs/vfx_projectile_01.tscn"
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_338y4"]
sky_energy_multiplier = 0.65
[sub_resource type="Sky" id="Sky_s2m6d"]
sky_material = SubResource("ProceduralSkyMaterial_338y4")
[sub_resource type="Environment" id="Environment_01qi8"]
background_mode = 2
background_energy_multiplier = 0.38
sky = SubResource("Sky_s2m6d")
glow_enabled = true
glow_levels/6 = 0.13
glow_levels/7 = 0.25
glow_intensity = 0.1
glow_blend_mode = 0
glow_hdr_threshold = 0.5
glow_hdr_scale = 1.5
volumetric_fog_enabled = true
volumetric_fog_density = 0.0811
volumetric_fog_albedo = Color(0.243137, 0.243137, 0.243137, 1)
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_s4vrp"]
dof_blur_far_enabled = true
dof_blur_far_distance = 100.0
[sub_resource type="BoxMesh" id="BoxMesh_vf5wh"]
size = Vector3(150, 0, 150)
[sub_resource type="BoxShape3D" id="BoxShape3D_4gkui"]
size = Vector3(150, 1, 150)
[sub_resource type="BoxMesh" id="BoxMesh_appbf"]
[sub_resource type="BoxShape3D" id="BoxShape3D_nckwn"]
size = Vector3(1.34387, 1, 1)
[node name="World" type="Node3D" node_paths=PackedStringArray("rotate_to_mouse", "fire_point")]
script = ExtResource("1_idti0")
projectile_prefab = SubResource("Resource_h7mdb")
rotate_to_mouse = NodePath("Scene/CSGSphere3D")
fire_point = NodePath("Scene/CSGSphere3D/CSGBox3D/FirePoint")
[node name="Scene" type="Node" parent="."]
[node name="WorldEnvironment" type="WorldEnvironment" parent="Scene"]
environment = SubResource("Environment_01qi8")
camera_attributes = SubResource("CameraAttributesPractical_s4vrp")
[node name="Camera3D" type="Camera3D" parent="Scene/WorldEnvironment"]
transform = Transform3D(1, 0, 0, 0, 0.941294, 0.337588, 0, -0.337588, 0.941294, 0, 2.72096, 3.90772)
script = ExtResource("3_ynfkw")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 0.84934, 0.527846, 0, -0.527846, 0.84934, 0, 2.9349, 0)
light_energy = 0.5
[node name="Mountains" type="MeshInstance3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1.27348, 0, 0, 0, 1.26814, -21.5595, -3.87235, 0)
mesh = ExtResource("4_qjsfk")
skeleton = NodePath("../..")
[node name="Ground" type="StaticBody3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Scene/Ground"]
mesh = SubResource("BoxMesh_vf5wh")
skeleton = NodePath("../../..")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/Ground"]
shape = SubResource("BoxShape3D_4gkui")
[node name="Wall" type="StaticBody3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 11.0298, 0, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Scene/Wall"]
transform = Transform3D(1, 0, 0, 0, 5.4, 0, 0, 0, 11, 0.470179, 1.658, 0)
mesh = SubResource("BoxMesh_appbf")
skeleton = NodePath("../../..")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/Wall"]
transform = Transform3D(1, 0, 0, 0, 5.4, 0, 0, 0, 11, 0.298243, 1.658, 0)
shape = SubResource("BoxShape3D_nckwn")
[node name="SpotLight3D" type="SpotLight3D" parent="Scene"]
transform = Transform3D(0.714169, 0.238575, -0.658061, 0.196049, 0.834321, 0.515241, 0.671958, -0.496982, 0.549074, 3.08513, 6.69863, 4.07749)
spot_range = 60.629
spot_attenuation = 0.67
spot_angle = 55.0
spot_angle_attenuation = 2.82843
[node name="Camera3D" type="Camera3D" parent="Scene"]
transform = Transform3D(0.827962, 0.25459, -0.499662, 0, 0.891007, 0.45399, 0.560784, -0.375887, 0.73772, -8.97652, 8.79728, 11.4697)
current = true
fov = 50.0
[node name="CSGSphere3D" type="CSGSphere3D" parent="Scene" node_paths=PackedStringArray("camera")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10, 1, 0)
script = ExtResource("5_5wsiv")
camera = NodePath("../Camera3D")
rotation_speed = 50.0
[node name="CSGBox3D" type="CSGBox3D" parent="Scene/CSGSphere3D"]
transform = Transform3D(0.2, 0, 0, 0, 0.2, 0, 0, 0, 0.2, 0, 0, -0.6)
[node name="FirePoint" type="Node3D" parent="Scene/CSGSphere3D/CSGBox3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1.0331)
+73
View File
@@ -0,0 +1,73 @@
[gd_scene load_steps=10 format=3 uid="uid://cvb1vgt52x0cy"]
[ext_resource type="Script" uid="uid://dq7h3nectp4hy" path="res://Scripts/camera.gd" id="1_r84vf"]
[ext_resource type="ArrayMesh" uid="uid://cmswy8js5ghvq" path="res://Models/Mountains.obj" id="2_tbjhe"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_338y4"]
sky_energy_multiplier = 0.65
[sub_resource type="Sky" id="Sky_s2m6d"]
sky_material = SubResource("ProceduralSkyMaterial_338y4")
[sub_resource type="Environment" id="Environment_01qi8"]
background_mode = 2
background_energy_multiplier = 0.38
sky = SubResource("Sky_s2m6d")
glow_enabled = true
glow_levels/6 = 0.13
glow_levels/7 = 0.25
glow_intensity = 0.1
glow_blend_mode = 0
glow_hdr_threshold = 0.5
glow_hdr_scale = 1.5
volumetric_fog_enabled = true
volumetric_fog_density = 0.0811
volumetric_fog_albedo = Color(0.243137, 0.243137, 0.243137, 1)
[sub_resource type="CameraAttributesPractical" id="CameraAttributesPractical_s4vrp"]
dof_blur_far_enabled = true
dof_blur_far_distance = 100.0
[sub_resource type="BoxMesh" id="BoxMesh_vf5wh"]
size = Vector3(150, 0, 150)
[sub_resource type="BoxShape3D" id="BoxShape3D_4gkui"]
size = Vector3(150, 1, 150)
[sub_resource type="BoxMesh" id="BoxMesh_appbf"]
[node name="World" type="Node3D"]
[node name="Scene" type="Node" parent="."]
[node name="WorldEnvironment" type="WorldEnvironment" parent="Scene"]
environment = SubResource("Environment_01qi8")
camera_attributes = SubResource("CameraAttributesPractical_s4vrp")
[node name="Camera3D" type="Camera3D" parent="Scene/WorldEnvironment"]
transform = Transform3D(1, 0, 0, 0, 0.941294, 0.337588, 0, -0.337588, 0.941294, 0, 2.72096, 3.90772)
script = ExtResource("1_r84vf")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 0.84934, 0.527846, 0, -0.527846, 0.84934, 0, 2.9349, 0)
light_energy = 0.5
[node name="Mountains" type="MeshInstance3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1.27348, 0, 0, 0, 1.26814, -21.5595, -3.87235, 0)
mesh = ExtResource("2_tbjhe")
skeleton = NodePath("../..")
[node name="Ground" type="StaticBody3D" parent="Scene"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="Scene/Ground"]
mesh = SubResource("BoxMesh_vf5wh")
skeleton = NodePath("../../..")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/Ground"]
shape = SubResource("BoxShape3D_4gkui")
[node name="Cube" type="MeshInstance3D" parent="Scene"]
transform = Transform3D(0.7, 0, 0, 0, 2, 0, 0, 0, 0.7, -3.27909, -0.131935, 0)
mesh = SubResource("BoxMesh_appbf")
skeleton = NodePath("../..")
+54
View File
@@ -0,0 +1,54 @@
@tool
extends Node3D
@export var play_particles: bool = false:
set(value):
if value:
_find_particles() # Always scan for new particles
_cleanup_delays() # Remove deleted particles from the list
_start_delays() # Ensure delays exist for new particles
_play_particles()
play_particles = false # Reset the checkbox immediately
var particle_systems: Array
@export var delays: Dictionary
@export var cleanDelays: bool = false:
set(value):
if value:
delays.clear()
cleanDelays = false
func _find_particles():
particle_systems.clear() # Always clear and re-detect
var existing_delays = delays.duplicate() # Preserve existing delays
for child in get_children():
if child is GPUParticles3D:
particle_systems.append(child)
if not existing_delays.has(child.name):
existing_delays[child.name] = 0.0 # Assign default delay
delays = existing_delays # Restore old delays + new ones
func _cleanup_delays(): # Remove delays for particles that no longer exist
var valid_particle_names = particle_systems.map(func(p): return p.name)
for key in delays.keys():
if key not in valid_particle_names:
delays.erase(key) # Remove old particle system references
func _start_delays():
for particle in particle_systems:
if not delays.has(particle.name):
delays[particle.name] = 0.0 # Keep existing delays, add new ones
func _play_particles():
for particle in particle_systems:
var delay = delays.get(particle.name, 0.0)
_playDelay(particle, delay)
func _playDelay(ps: GPUParticles3D, delay: float):
ps.emitting = false # Reset first (prevents issues)
await get_tree().create_timer(delay).timeout
ps.restart() # Works in play mode
ps.emitting = true # Start emitting
+1
View File
@@ -0,0 +1 @@
uid://dxhfn5g0spoim
+68
View File
@@ -0,0 +1,68 @@
extends RigidBody3D
@export var speed: float = 10.0
@export var muzzle_prefab: PackedScene
@export var impact_prefab: PackedScene
@export var delay_trails_start: float = 0.1
@export var trails: Array[GPUTrail3D]
var direction: Vector3 = Vector3.FORWARD
var fire_point_position: Vector3
func set_fire_point(fp_position: Vector3):
fire_point_position = fp_position
func _ready():
delay_trails()
spawn_muzzle()
contact_monitor = true
max_contacts_reported = 1
func _integrate_forces(state):
linear_velocity = direction * speed
var collision = state.get_contact_count()
if collision > 0:
var contact = state.get_contact_collider_position(0)
var normal = state.get_contact_local_normal(0)
spawn_impact(contact, normal)
queue_free()
func spawn_muzzle():
if muzzle_prefab:
var muzzle_instance = muzzle_prefab.instantiate()
call_deferred("add_child", muzzle_instance)
await get_tree().create_timer(0.001).timeout
muzzle_instance.global_transform.origin = fire_point_position
muzzle_instance.play_particles = true
await get_tree().create_timer(3.0).timeout
if is_instance_valid(muzzle_instance):
muzzle_instance.queue_free()
func spawn_impact(contact_point: Vector3 = Vector3.ZERO, contact_normal: Vector3 = Vector3.ZERO):
if impact_prefab:
var impact_instance = impact_prefab.instantiate()
get_tree().current_scene.add_child(impact_instance)
impact_instance.play_particles = true
impact_instance.global_transform.origin = contact_point
impact_instance.look_at(contact_point + contact_normal, Vector3.UP)
await get_tree().create_timer(3.0).timeout
if is_instance_valid(impact_instance):
impact_instance.queue_free()
func delay_trails():
if trails.is_empty():
return
for trail in trails:
if is_instance_valid(trail):
trail.speed_scale = 0 # Stop movement initially, avoids glitches
await get_tree().create_timer(delay_trails_start).timeout
for trail in trails:
if is_instance_valid(trail):
trail.speed_scale = 1 # Resume movement
+1
View File
@@ -0,0 +1 @@
uid://srxbm3tbdhyu
+19
View File
@@ -0,0 +1,19 @@
extends Node3D
@export var camera: Camera3D = null
@export var rotation_speed: float = 5.0
func _process(delta):
if camera:
var mouse_pos = get_viewport().get_mouse_position()
var ray_origin = camera.project_ray_origin(mouse_pos)
var ray_dir = camera.project_ray_normal(mouse_pos)
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_dir * 1000.0)
var result = space_state.intersect_ray(query)
var target_position = result["position"] if result else ray_origin + ray_dir * 10.0
var direction = (target_position - global_transform.origin).normalized()
var target_rotation = Quaternion(Vector3.FORWARD, direction)
transform.basis = Basis(transform.basis.get_rotation_quaternion().slerp(target_rotation, delta * rotation_speed))
+1
View File
@@ -0,0 +1 @@
uid://b86hxdolpatb8
+37
View File
@@ -0,0 +1,37 @@
extends Node3D
@export var camera: Camera3D = null
@export var fire_rate: float = 0.5
@export var aoe_lifetime: float = 7.0
@export var aoe_prefab: PackedScene
var can_fire: bool = true
func _process(_delta):
if Input.is_action_pressed("fire") and can_fire:
fire_aoe()
can_fire = false
await get_tree().create_timer(fire_rate).timeout
can_fire = true
func fire_aoe():
if not (aoe_prefab and camera):
return
var mouse_position = get_viewport().get_mouse_position()
var ray_origin = camera.project_ray_origin(mouse_position)
var ray_direction = camera.project_ray_normal(mouse_position) * 1000 # Long ray
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_direction)
var result = space_state.intersect_ray(query)
if result.has("position"):
var aoe_instance = aoe_prefab.instantiate()
get_parent().add_child(aoe_instance)
aoe_instance.global_position = result["position"] # Spawn at hit position
aoe_instance.play_particles = true # Start particle effect
await get_tree().create_timer(aoe_lifetime).timeout
if is_instance_valid(aoe_instance):
aoe_instance.queue_free()
+1
View File
@@ -0,0 +1 @@
uid://bkdqbagbitktv
+31
View File
@@ -0,0 +1,31 @@
extends Node3D
@export var fire_rate: float = 0.5
@export var projectile_lifetime: float = 4.0
@export var projectile_prefab: PackedScene
@export var rotate_to_mouse: Node3D
@export var fire_point: Node3D
var can_fire: bool = true
func _process(_delta):
if Input.is_action_pressed("fire") and can_fire:
fire_projectile()
can_fire = false
await get_tree().create_timer(fire_rate).timeout
can_fire = true
func fire_projectile():
if projectile_prefab and fire_point and rotate_to_mouse:
var projectile_instance = projectile_prefab.instantiate()
get_parent().add_child(projectile_instance)
projectile_instance.global_position = fire_point.global_position
projectile_instance.set_fire_point(fire_point.global_transform.origin)
var direction = -rotate_to_mouse.global_transform.basis.z.normalized()
projectile_instance.direction = direction
projectile_instance.look_at(fire_point.global_position + -direction)
await get_tree().create_timer(projectile_lifetime).timeout
if projectile_instance:
projectile_instance.queue_free()
+1
View File
@@ -0,0 +1 @@
uid://gs00msx4i83u
+116
View File
@@ -0,0 +1,116 @@
class_name FreeLookCamera extends Camera3D
# Modifier keys' speed multiplier
const SHIFT_MULTIPLIER = 2.5
const ALT_MULTIPLIER = 1.0 / SHIFT_MULTIPLIER
@export_range(0.0, 1.0) var sensitivity: float = 0.25
# Mouse state
var _mouse_position = Vector2(0.0, 0.0)
var _total_pitch = 0.0
# Movement state
var _direction = Vector3(0.0, 0.0, 0.0)
var _velocity = Vector3(0.0, 0.0, 0.0)
var _acceleration = 30
var _deceleration = -10
var _vel_multiplier = 4
# Keyboard state
var _w = false
var _s = false
var _a = false
var _d = false
var _q = false
var _e = false
var _shift = false
var _alt = false
func _input(event):
# Receives mouse motion
if event is InputEventMouseMotion:
_mouse_position = event.relative
# Receives mouse button input
if event is InputEventMouseButton:
match event.button_index:
MOUSE_BUTTON_RIGHT: # Only allows rotation if right click down
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED if event.pressed else Input.MOUSE_MODE_VISIBLE)
MOUSE_BUTTON_WHEEL_UP: # Increases max velocity
_vel_multiplier = clamp(_vel_multiplier * 1.1, 0.2, 20)
MOUSE_BUTTON_WHEEL_DOWN: # Decereases max velocity
_vel_multiplier = clamp(_vel_multiplier / 1.1, 0.2, 20)
# Receives key input
if event is InputEventKey:
match event.keycode:
KEY_W:
_w = event.pressed
KEY_S:
_s = event.pressed
KEY_A:
_a = event.pressed
KEY_D:
_d = event.pressed
KEY_Q:
_q = event.pressed
KEY_E:
_e = event.pressed
KEY_SHIFT:
_shift = event.pressed
KEY_ALT:
_alt = event.pressed
# Updates mouselook and movement every frame
func _process(delta):
_update_mouselook()
_update_movement(delta)
# Updates camera movement
func _update_movement(delta):
# Computes desired direction from key states
_direction = Vector3(
(_d as float) - (_a as float),
(_e as float) - (_q as float),
(_s as float) - (_w as float)
)
# Computes the change in velocity due to desired direction and "drag"
# The "drag" is a constant acceleration on the camera to bring it's velocity to 0
var offset = _direction.normalized() * _acceleration * _vel_multiplier * delta \
+ _velocity.normalized() * _deceleration * _vel_multiplier * delta
# Compute modifiers' speed multiplier
var speed_multi = 1
if _shift: speed_multi *= SHIFT_MULTIPLIER
if _alt: speed_multi *= ALT_MULTIPLIER
# Checks if we should bother translating the camera
if _direction == Vector3.ZERO and offset.length_squared() > _velocity.length_squared():
# Sets the velocity to 0 to prevent jittering due to imperfect deceleration
_velocity = Vector3.ZERO
else:
# Clamps speed to stay within maximum value (_vel_multiplier)
_velocity.x = clamp(_velocity.x + offset.x, -_vel_multiplier, _vel_multiplier)
_velocity.y = clamp(_velocity.y + offset.y, -_vel_multiplier, _vel_multiplier)
_velocity.z = clamp(_velocity.z + offset.z, -_vel_multiplier, _vel_multiplier)
translate(_velocity * delta * speed_multi)
# Updates mouse look
func _update_mouselook():
# Only rotates mouse if the mouse is captured
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
_mouse_position *= sensitivity
var yaw = _mouse_position.x
var pitch = _mouse_position.y
_mouse_position = Vector2(0, 0)
# Prevents looking up/down too far
pitch = clamp(pitch, -90 - _total_pitch, 90 - _total_pitch)
_total_pitch += pitch
rotate_y(deg_to_rad(-yaw))
rotate_object_local(Vector3(1,0,0), deg_to_rad(-pitch))
+1
View File
@@ -0,0 +1 @@
uid://dq7h3nectp4hy
+1
View 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
View File
@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://f2tnjkmyapp0"
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
+28
View File
@@ -0,0 +1,28 @@
; 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="GAP_VFX_BeginnerCourse"
run/main_scene="uid://jf6lhabdtxqs"
config/features=PackedStringArray("4.4", "Forward Plus")
config/icon="res://icon.svg"
[dotnet]
project/assembly_name="VFX_Fire"
[input]
fire={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}