Added Glitch Room Source code to Gitty

This commit is contained in:
Jeff Luke committed 2026-10-04 11:31:17 -07:00
commit 1ac9eb5644
505 files changed
+28504

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
[gd_scene format=3 uid="uid://crtp0cdh3x55h"]
[ext_resource type="Script" uid="uid://dxkm5iqysxkyy" path="res://games/missile_command/missile_command.gd" id="1"]
[ext_resource type="Script" uid="uid://ed0yaf45xgff" path="res://games/missile_command/pixel_board.gd" id="2"]
[ext_resource type="Script" uid="uid://ci3wofmlmkbbg" path="res://games/missile_command/display.gd" id="3"]
[ext_resource type="Script" uid="uid://dsft8skyj7r8x" path="res://games/missile_command/arcade_audio.gd" id="4"]
[node name="MissileCommand" type="Node" unique_id=2072674313]
script = ExtResource("1")
[node name="PixelScreen" type="SubViewport" parent="." unique_id=1549979971]
disable_3d = true
size = Vector2i(256, 231)
render_target_update_mode = 4
[node name="Board" type="Node2D" parent="PixelScreen" unique_id=1583118688]
script = ExtResource("2")
[node name="Display" type="Node2D" parent="." unique_id=1661990548]
texture_filter = 1
script = ExtResource("3")
[node name="Audio" type="Node" parent="." unique_id=2862985]
script = ExtResource("4")
+42
View File
@@ -0,0 +1,42 @@
extends Node
## Small synthesized arcade effects; no external audio assets required.
var voices: Array[AudioStreamPlayer] = []
var sounds: Dictionary = {}
var voice_index := 0
func _ready() -> void:
for i in range(6):
var voice := AudioStreamPlayer.new()
voice.volume_db = -17.0
add_child(voice)
voices.append(voice)
sounds["fire"] = tone(1300.0, 220.0, 0.09)
sounds["hit"] = tone(170.0, 40.0, 0.18)
sounds["zap"] = tone(1700.0, 70.0, 0.55)
sounds["death"] = tone(440.0, 35.0, 0.7)
sounds["level"] = tone(220.0, 880.0, 0.4)
func tone(start: float, finish: float, duration: float) -> AudioStreamWAV:
var samples := PackedByteArray()
var count := int(22050 * duration)
samples.resize(count * 2)
var phase := 0.0
for i in range(count):
var t := float(i) / count
phase += lerpf(start, finish, t) * TAU / 22050.0
var envelope := minf(t * 50.0, 1.0) * pow(1.0 - t, 1.5)
var value := int((sin(phase) + sin(phase * 2.0) * 0.25) * envelope * 18000)
samples.encode_s16(i * 2, value)
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_16_BITS
stream.mix_rate = 22050
stream.data = samples
return stream
func play(effect: String) -> void:
if not sounds.has(effect) or voices.is_empty():
return
var voice := voices[voice_index]
voice_index = (voice_index + 1) % voices.size()
voice.stream = sounds[effect]
voice.play()
func stop() -> void:
for voice in voices:
voice.stop()
@@ -0,0 +1 @@
uid://dsft8skyj7r8x
+18
View File
@@ -0,0 +1,18 @@
@tool
extends Node2D
const LOGICAL := Vector2(256,231)
var destination := Rect2(0,0,768,576)
func _process(_delta: float) -> void:
var available := get_viewport_rect().size if not Engine.is_editor_hint() else Vector2(768,576)
var factor := minf(available.x/4.0,available.y/3.0)
var size := Vector2(4,3)*factor
destination = Rect2((available-size)*.5,size)
queue_redraw()
func to_logical(point: Vector2) -> Vector2:
return (point-destination.position)/destination.size*LOGICAL
func to_logical_delta(delta: Vector2) -> Vector2:
return delta/destination.size*LOGICAL
func _draw() -> void:
draw_rect(Rect2(Vector2.ZERO,get_viewport_rect().size),Color.BLACK)
if has_node("../PixelScreen"):
draw_texture_rect(get_node("../PixelScreen").get_texture(),destination,false)
+1
View File
@@ -0,0 +1 @@
uid://ci3wofmlmkbbg
+283
View File
@@ -0,0 +1,283 @@
@tool
extends ArcadeGame
## All positions use the 256 x 231 logical arcade screen.
@export_range(0,99) var waves_to_win: int = 3
@export_range(.5,2.0) var difficulty: float = 1.0
@export_range(1000,50000,1000) var bonus_city_interval: int = 10000
@export var relative_mouse_aim: bool = false
@export_range(.1,4.0) var trackball_sensitivity: float = 1.0
const CITY_X := [43,68,93,163,188,213]
const BASE_X := [16,128,240]
var phase := "title"
var phase_time := 0.0
var clock := 0.0
var attract_time := 0.0
var wave := 1
var score := 0
var high_score := 0
var bonus_bank := 0
var next_bonus := 10000
var cities: Array[bool] = [true,true,true,true,true,true]
var bases: Array[bool] = [true,true,true]
var ammo: Array[int] = [10,10,10]
var cooldown: Array[float] = [0.0,0.0,0.0]
var aim := Vector2(128,100)
var incoming: Array[Dictionary] = []
var interceptors: Array[Dictionary] = []
var blasts: Array[Dictionary] = []
var aircraft: Array[Dictionary] = []
var leaderboard: Array[int] = []
var remaining := 0
var spawn_time := 0.0
var aircraft_time := 0.0
var wave_bonus := 0
var recorded := false
var rng := RandomNumberGenerator.new()
@onready var board: Node2D = $PixelScreen/Board
@onready var display: Node2D = $Display
func _ready() -> void:
if not Engine.is_editor_hint(): rng.randomize()
func start_game() -> bool:
if not super.start_game(): return false
wave = 1
score = 0
bonus_bank = 0
next_bonus = bonus_city_interval
recorded = false
cities.fill(true)
incoming.clear()
interceptors.clear()
blasts.clear()
aircraft.clear()
_begin_wave()
return true
func reset_game() -> void:
super.reset_game()
$Audio.stop()
phase = "title"
attract_time = 0
phase_time = 0
score = 0
wave = 1
cities.fill(true)
bases.fill(true)
ammo.fill(10)
cooldown.fill(0)
incoming.clear()
interceptors.clear()
blasts.clear()
aircraft.clear()
bonus_bank = 0
func _begin_wave() -> void:
bases.fill(true)
ammo.fill(10)
cooldown.fill(0)
remaining = 12 + mini(wave,20)*3
spawn_time = 1.0
aircraft_time = 9.0
phase = "ready"
phase_time = 2.2
wave_bonus = 0
func add_trackball_motion(motion: Vector2) -> void:
set_aim(aim+motion*trackball_sensitivity)
func set_aim(point: Vector2) -> void:
aim = point.clamp(Vector2(1,25),Vector2(254,199))
func _action(event: InputEvent, action: String) -> bool:
return event.is_action_pressed(action)
func _unhandled_input(event: InputEvent) -> void:
if Engine.is_editor_hint(): return
if event is InputEventMouseMotion:
if relative_mouse_aim:
add_trackball_motion(display.to_logical_delta(event.relative))
else:
set_aim(display.to_logical(event.position))
if event is InputEventKey and event.echo: return
if _action(event,"arcade_start"):
if not is_running: start_game()
return
if phase != "play": return
if event is InputEventMouseButton and not relative_mouse_aim:
set_aim(display.to_logical(event.position))
for i in range(3):
if _action(event,"arcade_action_%d" % (i+1)):
fire(i)
return
func fire(base: int) -> bool:
if phase != "play" or base < 0 or base > 2 or not bases[base] or ammo[base] <= 0 or cooldown[base] > 0: return false
ammo[base] -= 1
cooldown[base] = .12
var origin := Vector2(BASE_X[base],202)
interceptors.append({"origin":origin,"pos":origin,"target":aim,"speed":180.0 if base == 1 else 120.0})
$Audio.play("fire")
return true
func multiplier() -> int:
return mini(6,1+int((wave-1)/2))
func add_score(amount: int) -> void:
score += amount
high_score = maxi(high_score,score)
while score >= next_bonus:
bonus_bank += 1
next_bonus += bonus_city_interval
func _record_score() -> void:
if recorded: return
recorded = true
leaderboard.append(score)
leaderboard.sort()
leaderboard.reverse()
if leaderboard.size() > 8: leaderboard.resize(8)
func _physics_process(delta: float) -> void:
clock += delta
if Engine.is_editor_hint():
board.queue_redraw()
return
for i in range(3): cooldown[i] = maxf(0,cooldown[i]-delta)
match phase:
"title","leaderboard":
attract_time += delta
if phase == "title" and attract_time >= 9.0:
phase = "leaderboard"
attract_time = 0.0
elif phase == "leaderboard" and attract_time >= 7.0:
phase = "title"
attract_time = 0.0
"ready":
phase_time -= delta
if phase_time <= 0: phase = "play"
"play": _step(delta)
"bonus":
phase_time -= delta
if phase_time <= 0:
if waves_to_win > 0 and wave >= waves_to_win:
phase = "won"
_record_score()
win_game()
else:
wave += 1
_begin_wave()
"lost":
phase_time -= delta
if phase_time <= 0:
phase = "title"
attract_time = 0
board.queue_redraw()
func target_point() -> Vector2:
var candidates: Array[Vector2] = []
for i in range(6):
if cities[i]: candidates.append(Vector2(CITY_X[i],211))
for i in range(3):
if bases[i]: candidates.append(Vector2(BASE_X[i],204))
return candidates[rng.randi_range(0,candidates.size()-1)] if not candidates.is_empty() else Vector2(128,212)
func spawn_missile(origin: Vector2, kind: String = "missile") -> void:
incoming.append({"origin":origin,"pos":origin,"target":target_point(),"kind":kind,"speed":(11.0+mini(wave,25)*1.8)*difficulty,"split":kind == "splitter","alive":true})
func explosion(point: Vector2, hostile: bool = false, radius: float = 18.0) -> void:
blasts.append({"pos":point,"age":0.0,"radius":0.0,"max":radius,"hostile":hostile})
if phase == "play": $Audio.play("hit")
func _restore_cities() -> void:
for i in range(6):
if not cities[i] and bonus_bank > 0:
cities[i] = true
bonus_bank -= 1
func _impact(point: Vector2) -> void:
for i in range(6):
if absf(point.x-CITY_X[i]) < 9: cities[i] = false
for i in range(3):
if absf(point.x-BASE_X[i]) < 12:
bases[i] = false
ammo[i] = 0
explosion(point,true,14)
func _step(delta: float) -> void:
spawn_time -= delta
if remaining > 0 and spawn_time <= 0:
var kind := "missile"
if wave >= 3 and remaining%5 == 0: kind = "splitter"
if wave >= 5 and remaining%7 == 0: kind = "smart"
spawn_missile(Vector2(rng.randf_range(4,252),24),kind)
remaining -= 1
spawn_time = maxf(.24,1.1-wave*.045)/difficulty
if wave >= 3 and remaining > 0:
aircraft_time -= delta
if aircraft_time <= 0:
aircraft.append({"pos":Vector2(-10,55+rng.randf()*35),"drop":1.0,"alive":true})
aircraft_time = 15
for shot in interceptors:
shot.pos = shot.pos.move_toward(shot.target,shot.speed*delta)
if shot.pos == shot.target: explosion(shot.target)
interceptors = interceptors.filter(func(s: Dictionary) -> bool: return s.pos != s.target)
for blast in blasts:
blast.age += delta
var age: float = blast.age
blast.radius = blast.max * (minf(1,age/.45) if age < 1.0 else maxf(0,(1.8-age)/.8))
blasts = blasts.filter(func(b: Dictionary) -> bool: return b.age < 1.8)
var children: Array[Vector2] = []
for missile in incoming:
var previous: Vector2 = missile.pos
var destination: Vector2 = missile.target
if missile.kind == "smart":
for blast in blasts:
if not blast.hostile and missile.pos.distance_to(blast.pos) < blast.radius+15:
destination.x += 60 if missile.pos.x >= blast.pos.x else -60
missile.pos = missile.pos.move_toward(destination,missile.speed*delta).clamp(Vector2(0,24),Vector2(255,214))
for blast in blasts:
if blast.hostile: continue
var nearest := Geometry2D.get_closest_point_to_segment(blast.pos,previous,missile.pos)
if nearest.distance_to(blast.pos) <= blast.radius+1:
missile.alive = false
add_score((125 if missile.kind == "smart" else 25)*multiplier())
break
if not missile.alive:
explosion(missile.pos,false,13)
elif missile.pos.y >= 202 and missile.pos.distance_to(missile.target) < 12:
missile.alive = false
_impact(missile.target)
elif missile.split and missile.pos.y > 90:
missile.split = false
children.append(missile.pos)
incoming = incoming.filter(func(m: Dictionary) -> bool: return m.alive)
for point in children:
spawn_missile(point)
spawn_missile(point)
for plane in aircraft:
plane.pos.x += delta*(18+wave)
plane.drop -= delta
if plane.drop <= 0:
spawn_missile(plane.pos)
plane.drop = 3
for blast in blasts:
if not blast.hostile and plane.pos.distance_to(blast.pos) < blast.radius+4:
plane.alive = false
add_score(100*multiplier())
explosion(plane.pos)
break
aircraft = aircraft.filter(func(p: Dictionary) -> bool: return p.alive and p.pos.x < 268)
if not cities.has(true) and bonus_bank == 0:
phase = "lost"
phase_time = 5
_record_score()
lose_game()
elif remaining == 0 and incoming.is_empty() and interceptors.is_empty() and blasts.is_empty() and aircraft.is_empty():
wave_bonus = (cities.count(true)*100+(ammo[0]+ammo[1]+ammo[2])*5)*multiplier()
add_score(wave_bonus)
_restore_cities()
phase = "bonus"
phase_time = 3.0
$Audio.play("level")
@@ -0,0 +1 @@
uid://dxkm5iqysxkyy
+211
View File
@@ -0,0 +1,211 @@
@tool
extends Node2D
const SIZE := Vector2(256,231)
const FONT := {
"A":[14,17,17,31,17,17,17],"B":[30,17,17,30,17,17,30],"C":[14,17,16,16,16,17,14],"D":[30,17,17,17,17,17,30],"E":[31,16,16,30,16,16,31],"F":[31,16,16,30,16,16,16],"G":[14,17,16,23,17,17,15],"H":[17,17,17,31,17,17,17],"I":[31,4,4,4,4,4,31],"J":[7,2,2,2,18,18,12],"K":[17,18,20,24,20,18,17],"L":[16,16,16,16,16,16,31],"M":[17,27,21,21,17,17,17],"N":[17,25,21,19,17,17,17],"O":[14,17,17,17,17,17,14],"P":[30,17,17,30,16,16,16],"Q":[14,17,17,17,21,18,13],"R":[30,17,17,30,20,18,17],"S":[15,16,16,14,1,1,30],"T":[31,4,4,4,4,4,4],"U":[17,17,17,17,17,17,14],"V":[17,17,17,17,17,10,4],"W":[17,17,17,21,21,21,10],"X":[17,17,10,4,10,17,17],"Y":[17,17,10,4,4,4,4],"Z":[31,1,2,4,8,16,31],
"0":[14,17,19,21,25,17,14],"1":[4,12,4,4,4,4,14],"2":[14,17,1,2,4,8,31],"3":[30,1,1,14,1,1,30],"4":[2,6,10,18,31,2,2],"5":[31,16,16,30,1,1,30],"6":[14,16,16,30,17,17,14],"7":[31,1,2,4,8,8,8],"8":[14,17,17,14,17,17,14],"9":[14,17,17,15,1,1,14],"!":[4,4,4,4,4,0,4],"-":[0,0,0,31,0,0,0],".":[0,0,0,0,0,12,12],":":[0,12,12,0,12,12,0]
}
var game: Node
var mesh := ArrayMesh.new()
var vertices := PackedVector3Array()
var colors := PackedColorArray()
var indices := PackedInt32Array()
var logo_pixels: Array[Vector2i] = []
var title_craters: Array[Dictionary] = []
var title_rows: Dictionary = {}
func _ready() -> void:
game = get_parent().get_parent()
_build_logo()
func _build_logo() -> void:
for row in range(2):
var word := "MISSILE" if row == 0 else "COMMAND"
for letter in range(word.length()):
for y in range(7):
for x in range(5):
if FONT[word[letter]][y] & (1 << (4-x)):
for sy in range(4):
for sx in range(4):
logo_pixels.append(Vector2i(46+letter*24+x*4+sx,65+row*43+y*4+sy))
# Each crater has a deterministic impact time and persists as missing pixels.
for i in range(42):
title_craters.append({"pos":Vector2(42+fposmod(i*59.7,174),62+fposmod(i*37.3,79)),"time":1.0+i*.145,"radius":9.0+float(i%5)})
for row in range(2):
for letter in range(7):
title_craters.append({"pos":Vector2(56+letter*24,79+row*43),"time":6.2+letter*.1,"radius":20.0})
# Precompute erosion times once, not a pixel/crater search every frame.
for point in logo_pixels:
var erased_at := 7.7
for crater in title_craters:
var distance := Vector2(point).distance_to(crater.pos)
if distance < crater.radius:
erased_at = minf(erased_at,crater.time+distance/30.0)
if not title_rows.has(point.y): title_rows[point.y] = []
title_rows[point.y].append(Vector2(point.x,erased_at))
for y in title_rows:
title_rows[y].sort_custom(func(a: Vector2,b: Vector2) -> bool: return a.x < b.x)
func rect(position: Vector2, size: Vector2, color: Color) -> void:
var start := vertices.size()
for point in [position,position+Vector2(size.x,0),position+size,position+Vector2(0,size.y)]:
vertices.append(Vector3(floorf(point.x),floorf(point.y),0))
colors.append(color)
indices.append_array(PackedInt32Array([start,start+1,start+2,start,start+2,start+3]))
func segment(a: Vector2,b: Vector2,color: Color) -> void:
var count := maxi(1,int(maxf(absf(b.x-a.x),absf(b.y-a.y))))
for i in range(count+1): rect(a.lerp(b,float(i)/count).round(),Vector2.ONE,color)
func disk(center: Vector2,radius: float,color: Color) -> void:
var r := int(radius)
for y in range(-r,r+1):
var extent := int(sqrt(maxf(0,r*r-y*y)))
rect(center+Vector2(-extent,y),Vector2(extent*2+1,1),color)
func text_at(value: String,at: Vector2,color: Color,size: int = 1) -> void:
for i in range(value.length()):
if not FONT.has(value[i]): continue
for y in range(7):
var run := -1
for x in range(6):
var filled: bool = x < 5 and (FONT[value[i]][y] & (1 << (4-x))) != 0
if filled and run < 0: run = x
if not filled and run >= 0:
rect(at+Vector2((i*6+run)*size,y*size),Vector2((x-run)*size,size),color)
run = -1
func label(value: String,y: float,color: Color,size: int = 1) -> void:
text_at(value,Vector2((256-(value.length()*6-1)*size)/2.0,y),color,size)
func palette() -> Array:
return [[Color("ffff00"),Color("203cff"),Color("ff2525")],[Color("20e5ff"),Color("ff3030"),Color("ffffff")],[Color("ff3030"),Color("24ff35"),Color("ffff00")]][int((game.wave-1)/2)%3]
func landscape() -> void:
var p := palette()
rect(Vector2(0,214),Vector2(256,17),p[0])
for i in range(3):
var x: float = game.BASE_X[i]
for y in range(12): rect(Vector2(x-8-y,202+y),Vector2(16+y*2,1),p[0])
if game.bases[i]:
var slots := [Vector2(0,-9),Vector2(-2,-6),Vector2(2,-6),Vector2(-4,-3),Vector2(0,-3),Vector2(4,-3),Vector2(-6,0),Vector2(-2,0),Vector2(2,0),Vector2(6,0)]
for missile in range(game.ammo[i]):
var at: Vector2 = Vector2(x,213)+slots[missile]
rect(at-Vector2(0,2),Vector2(1,3),p[1])
rect(at-Vector2(1,0),Vector2(3,1),p[1])
else: rect(Vector2(x-7,207),Vector2(14,7),Color.BLACK)
for i in range(6):
var x: float = game.CITY_X[i]
if game.cities[i]:
for building in range(7):
var h := 2+(building*3+i)%7
rect(Vector2(x-9+building*3,214-h),Vector2(2,h),p[1])
rect(Vector2(x-9,212),Vector2(20,2),p[1])
else:
rect(Vector2(x-9,213),Vector2(19,2),Color.BLACK)
func hud() -> void:
var p := palette()
text_at(str(game.score),Vector2(14,7),p[2])
text_at(str(game.high_score),Vector2(106,7),p[2])
text_at("1UP",Vector2(17,18),p[1])
text_at("HIGH",Vector2(112,18),p[1])
text_at("WAVE %d" % game.wave,Vector2(204,7),p[2])
func title() -> void:
var t: float = game.attract_time
var color := Color.from_hsv(fposmod(floor(t*5)/7,1),1,1)
for y in title_rows:
var run_start := -1
var run_end := -1
for pixel in title_rows[y]:
var x := int(pixel.x)
var visible: bool = t < pixel.y
if run_start >= 0 and (not visible or x > run_end+1):
rect(Vector2(run_start,y),Vector2(run_end-run_start+1,1),color)
run_start = -1
if visible:
if run_start < 0: run_start = x
run_end = x
if run_start >= 0: rect(Vector2(run_start,y),Vector2(run_end-run_start+1,1),color)
for crater in title_craters:
var age: float = t-crater.time
if age > 0 and age < .65:
disk(crater.pos,minf(9,age*25)*(1-age/.65),Color.from_hsv(fposmod(t*.7+.33,1),1,1))
label("PUSH START",174,Color("24ff35"))
label("MISSILE COMMAND",193,Color("2040ff"))
label("FREE PLAY",222,Color("ffff00"))
func _draw() -> void:
if game == null: return
vertices.clear()
colors.clear()
indices.clear()
draw_rect(Rect2(Vector2.ZERO,SIZE),Color.BLACK)
if game.phase == "title":
title()
elif game.phase == "leaderboard":
landscape()
hud()
label("HIGH SCORES",37,Color("2040ff"))
for i in range(8):
var value := "%06d" % game.leaderboard[i] if i < game.leaderboard.size() else "------"
text_at("P1" if i < game.leaderboard.size() else "--",Vector2(70,56+i*12),Color.YELLOW)
text_at(value,Vector2(126,56+i*12),Color.YELLOW)
label("BONUS CITY EVERY",163,Color("2040ff"))
label("%d POINTS" % game.bonus_city_interval,176,Color.YELLOW)
label("PUSH START",195,Color("2040ff"))
else:
landscape()
hud()
var p := palette()
for missile in game.incoming:
segment(missile.origin,missile.pos,p[2])
if missile.kind == "smart":
var at: Vector2 = missile.pos
segment(at+Vector2(0,-3),at+Vector2(2,0),Color.WHITE)
segment(at+Vector2(2,0),at+Vector2(0,3),Color.WHITE)
segment(at+Vector2(0,3),at+Vector2(-2,0),Color.WHITE)
segment(at+Vector2(-2,0),at+Vector2(0,-3),Color.WHITE)
else: rect(missile.pos,Vector2(2,2),Color.WHITE)
for shot in game.interceptors:
segment(shot.origin,shot.pos,p[1])
rect(shot.pos,Vector2(2,2),Color.WHITE)
for plane in game.aircraft:
var at: Vector2 = plane.pos
segment(at-Vector2(6,0),at+Vector2(6,0),p[2])
segment(at-Vector2(2,4),at+Vector2(2,4),p[2])
segment(at-Vector2(5,2),at-Vector2(5,-2),p[2])
for blast in game.blasts:
var color: Color = p[2] if int(game.clock*12)%3 != 0 else Color.WHITE
disk(blast.pos,blast.radius,color)
if blast.age > 1.0: disk(blast.pos,blast.radius*.5,Color.BLACK)
if game.phase in ["play","ready"]:
var at: Vector2 = game.aim.round()
segment(at-Vector2(3,0),at+Vector2(3,0),p[1])
segment(at-Vector2(0,3),at+Vector2(0,3),p[1])
if game.phase == "ready": label("DEFEND CITIES",181,p[1])
if game.phase == "bonus":
label("BONUS POINTS",85,p[1])
label(str(game.wave_bonus),102,p[0],2)
label("%d CITIES SAVED" % game.cities.count(true),131,p[1])
if game.phase == "lost":
disk(Vector2(128,114),minf(75,(5-game.phase_time)*24),p[2])
label("THE END",110,Color.BLACK,2)
if game.phase == "won":
label("YOU WIN!",100,p[0],2)
label("CITIES DEFENDED",123,p[1])
label("PUSH START",178,p[1])
mesh.clear_surfaces()
if not vertices.is_empty():
var arrays := []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertices
arrays[Mesh.ARRAY_COLOR] = colors
arrays[Mesh.ARRAY_INDEX] = indices
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,arrays)
draw_mesh(mesh,null)
+1
View File
@@ -0,0 +1 @@
uid://ed0yaf45xgff