Avatar customization: skin colour = swatches + shade slider
checkpoint / checkpoint (push) Successful in 1m45s
checkpoint / checkpoint (push) Successful in 1m45s
The single tone slider could never get back to the default yellow. Replaced it with a row of preset colour swatches (yellow first, then skin tones and a few fun colours) plus a "Shade" slider that darkens/lightens the picked swatch (0.5 = as-is). Picking yellow at mid shade reproduces the baked default exactly. Verified: avatar smoke 14/14, fresh-clone, and drove the swatches + shade in rendered screenshots (green skin, back to yellow, dark tan). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
@@ -76,6 +76,23 @@ var _preview_root: Node3D = null
|
||||
## Currently selected slot id.
|
||||
var _selected_slot: String = ""
|
||||
|
||||
## Skin: a base colour picked from a swatch, plus a shade multiplier from the
|
||||
## slider (0.5 = the swatch as-is, lower = darker, higher = lighter). The
|
||||
## first swatch is the placeholder default yellow.
|
||||
const SKIN_SWATCHES: Array[Color] = [
|
||||
Color(0.96, 0.80, 0.19), # yellow (default)
|
||||
Color(0.95, 0.78, 0.66), # pale
|
||||
Color(0.83, 0.62, 0.46), # tan
|
||||
Color(0.55, 0.38, 0.27), # brown
|
||||
Color(0.33, 0.21, 0.15), # deep brown
|
||||
Color(0.62, 0.62, 0.64), # grey
|
||||
Color(0.78, 0.24, 0.24), # red
|
||||
Color(0.30, 0.62, 0.34), # green
|
||||
Color(0.26, 0.46, 0.76), # blue
|
||||
Color(0.62, 0.36, 0.72), # purple
|
||||
]
|
||||
var _skin_base: Color = SKIN_SWATCHES[0]
|
||||
|
||||
## Turntable: the preview avatar's Y rotation (radians), whether the player is
|
||||
## currently dragging it, and how long since they last touched it (drives the
|
||||
## slow idle auto-spin). Starts facing the camera.
|
||||
@@ -289,14 +306,28 @@ func _build_ui() -> void:
|
||||
_item_list.add_theme_constant_override("separation", 4)
|
||||
item_scroll.add_child(_item_list)
|
||||
|
||||
# Skin tone control.
|
||||
# Skin colour: a row of preset swatches + a shade slider that darkens or
|
||||
# lightens the picked colour. The first swatch is the default yellow.
|
||||
var skin_header := Label.new()
|
||||
skin_header.text = "Skin Tone"
|
||||
skin_header.text = "Skin Colour"
|
||||
skin_header.add_theme_font_size_override("font_size", 16)
|
||||
controls_vbox.add_child(skin_header)
|
||||
|
||||
var swatch_grid := GridContainer.new()
|
||||
swatch_grid.columns = 5
|
||||
swatch_grid.add_theme_constant_override("h_separation", 4)
|
||||
swatch_grid.add_theme_constant_override("v_separation", 4)
|
||||
controls_vbox.add_child(swatch_grid)
|
||||
for c in SKIN_SWATCHES:
|
||||
swatch_grid.add_child(_make_swatch(c))
|
||||
|
||||
var shade_label := Label.new()
|
||||
shade_label.text = "Shade"
|
||||
shade_label.add_theme_font_size_override("font_size", 13)
|
||||
controls_vbox.add_child(shade_label)
|
||||
|
||||
_skin_tone_slider = HSlider.new()
|
||||
_skin_tone_slider.name = "SkinToneSlider"
|
||||
_skin_tone_slider.name = "ShadeSlider"
|
||||
_skin_tone_slider.min_value = 0.0
|
||||
_skin_tone_slider.max_value = 1.0
|
||||
_skin_tone_slider.step = 0.01
|
||||
@@ -486,17 +517,44 @@ func _refresh_preview() -> void:
|
||||
# Skin tone
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
func _on_skin_tone_changed(value: float) -> void:
|
||||
## Build one clickable colour swatch.
|
||||
func _make_swatch(color: Color) -> Button:
|
||||
var b := Button.new()
|
||||
b.custom_minimum_size = Vector2(40, 30)
|
||||
b.tooltip_text = "#" + color.to_html(false)
|
||||
for state in ["normal", "hover", "pressed", "focus", "disabled"]:
|
||||
var sb := StyleBoxFlat.new()
|
||||
sb.bg_color = color
|
||||
sb.set_corner_radius_all(3)
|
||||
if state == "hover" or state == "pressed" or state == "focus":
|
||||
sb.set_border_width_all(2)
|
||||
sb.border_color = Color.WHITE
|
||||
b.add_theme_stylebox_override(state, sb)
|
||||
b.pressed.connect(_on_skin_swatch_pressed.bind(color))
|
||||
return b
|
||||
|
||||
|
||||
func _on_skin_swatch_pressed(color: Color) -> void:
|
||||
_skin_base = color
|
||||
_apply_skin_colour()
|
||||
|
||||
|
||||
func _on_skin_tone_changed(_value: float) -> void:
|
||||
_apply_skin_colour()
|
||||
|
||||
|
||||
## Combine the picked swatch with the shade slider and push it to the loadout.
|
||||
func _apply_skin_colour() -> void:
|
||||
if loadout == null:
|
||||
return
|
||||
# Map slider 0..1 across a deep-to-pale skin range.
|
||||
var t := value
|
||||
loadout.skin_tone = Color(
|
||||
0.35 + 0.60 * t, # R
|
||||
0.22 + 0.55 * t, # G
|
||||
0.16 + 0.50 * t, # B
|
||||
1.0,
|
||||
)
|
||||
var shade: float = _skin_tone_slider.value if _skin_tone_slider != null else 0.5
|
||||
var col := _skin_base
|
||||
if shade < 0.5:
|
||||
col = _skin_base.lerp(Color.BLACK, (0.5 - shade) * 1.7)
|
||||
elif shade > 0.5:
|
||||
col = _skin_base.lerp(Color.WHITE, (shade - 0.5) * 1.3)
|
||||
col.a = 1.0
|
||||
loadout.skin_tone = col
|
||||
_refresh_preview()
|
||||
loadout_changed.emit(loadout)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user