๐ŸซงArt_011 Prefab Projectile Launcher

BamgasiJMยท2026๋…„ 3์›” 21์ผ

Unity GenArt

๋ชฉ๋ก ๋ณด๊ธฐ
22/41
post-thumbnail

๋žœ๋ค ๋ฒ ์ง€์–ด ๊ณก์„  ์œ„๋กœ ์œ ๋„ ํˆฌ์‚ฌ์ฒด ๊ตฌํ˜„

๊ฒŒ์ž„์˜ค๋ธŒ์ ํŠธ ์„ค์ •

  • Launcher ์˜ค๋ธŒ์ ํŠธ : ๋นˆ GameObject ์ƒ์„ฑ โ†’ ProjectileLauncher ์ปดํฌ๋„ŒํŠธ ๋ถ€์ฐฉ
  • Start Point : ๋นˆ GameObject ์ƒ์„ฑ(์˜ˆ: StartPoint) โ†’ Launcher์˜ startPoint ์Šฌ๋กฏ์— ๋“œ๋ž˜๊ทธ
  • Target Point : ๋นˆ GameObject ์ƒ์„ฑ(์˜ˆ: TargetPoint) โ†’ Launcher์˜ targetPoint ์Šฌ๋กฏ์— ๋“œ๋ž˜๊ทธ
  • Projectile Prefab : ํ๋ธŒ Mesh + URP Lit ๋จธํ‹ฐ๋ฆฌ์–ผ ์ ์šฉ ํ›„ Prefabํ™” โ†’ projectilePrefab ์Šฌ๋กฏ์— ๋“œ๋ž˜๊ทธ

Prefab ์„ค์ •

  • Mesh: ์›ํ•˜๋Š” ํˆฌ์‚ฌ์ฒด Prefab
  • Material: URP Lit ์…ฐ์ด๋”, Surface Type = Opaque
  • Rigidbody: ์—†์Œ (๋ฌผ๋ฆฌ ๋ถˆํ•„์š”)
  • Collider: ํ•„์š” ์—†์œผ๋ฉด ์ œ๊ฑฐ

Start / End Point์— ๋ถ€์œ ํ•˜๋Š” ๋ชจ์…˜ ์ ์šฉ

  • FloatingMotion.cs๋ฅผ ๋ถ™์—ฌ์„œ ๊ณต๊ฐ„์— ๋– ๋‹ค๋‹ˆ๋Š” ๋“ฏํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ถ€์—ฌํ•จ.

ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •

  • Travel Time 0.8 ~ 1.5 ๋ชฉํ‘œ๊นŒ์ง€ ๋„๋‹ฌ ์‹œ๊ฐ„(์ดˆ)
  • Rotation Speed 500 ~ 1000 ํˆฌ์‚ฌ์ฒด ์ž์ „ ์†๋„(ยฐ/s)
  • Curve Strength 4 ~ 10 ๋ฒ ์ง€์–ด ๊ณก๋ฅ  ๋žœ๋ค ๋ฒ”์œ„ โ€” ํด์ˆ˜๋ก ๊ฒฝ๋กœ๊ฐ€ ํœจ
  • Control Point Count 2 ๋ฒ ์ง€์–ด ์ œ์–ด์  ์ˆ˜ (1~4) โ€” ๋งŽ์„์ˆ˜๋ก S์ปค๋ธŒ ๊ฐ€๋Šฅ
  • Start Color (1, 0.4, 0.7, 1) ๋ฐœ์‚ฌ ์‹œ ํ•‘ํฌ
  • End Color (1, 1, 1, 1) ๋„๋‹ฌ ์‹œ ํ™”์ดํŠธ

์‹คํ–‰

  • Play Mode ์ง„์ž… โ†’ Space ์—ฐํƒ€๋กœ ํˆฌ์‚ฌ์ฒด ์—ฐ์† ๋ฐœ์‚ฌ
  • Curve Strength๋ฅผ Runtime์— ์กฐ์ ˆํ•˜๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฒฝ๋กœ ๋ณ€ํ™” ํ™•์ธ ๊ฐ€๋Šฅ
  • ํˆฌ์‚ฌ์ฒด๊ฐ€ Target์— ๋„๋‹ฌํ•˜๋ฉด ์ž๋™ Destroy โ€” Scene์ด ์˜ค์—ผ๋˜์ง€ ์•Š์Œ

Code

๐Ÿ“„ProjectileLauncher.cs

using UnityEngine;
using UnityEngine.InputSystem;

public class ProjectileLauncher : MonoBehaviour
{
  [Header("References")]
  public Transform startPoint;
  public Transform targetPoint;
  public GameObject projectilePrefab;

  [Header("Motion")]
  public float travelTime = 1.2f;
  public float rotationSpeed = 720f;

  [Header("Bezier Randomness")]
  [Range(0f, 20f)] public float curveStrength = 6f;
  [Range(1, 4)] public int controlPointCount = 2;

  [Header("Color")]
  public Color startColor = new Color(1f, 0.4f, 0.7f);
  public Color endColor = Color.white;

  void Update()
  {
    if (Keyboard.current.spaceKey.wasPressedThisFrame)
      Launch();
  }

  void Launch()
  {
    GameObject go = Instantiate(projectilePrefab, startPoint.position, Random.rotation);
    go.AddComponent<ProjectileMotion>().Init(
        startPoint.position, targetPoint.position,
        travelTime, rotationSpeed,
        curveStrength, controlPointCount,
        startColor, endColor
    );
  }
}

๐Ÿ“„ProjectileMotion.cs

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class ProjectileMotion : MonoBehaviour
{
  Vector3[] _pts;
  float _duration;
  float _rotSpeed;
  Color _colStart, _colEnd;
  float _elapsed;
  Material _mat;
  static readonly int ColorID = Shader.PropertyToID("_BaseColor");

  public void Init(Vector3 start, Vector3 target,
                   float duration, float rotSpeed,
                   float curveStrength, int cpCount,
                   Color colStart, Color colEnd)
  {
    _duration = duration;
    _rotSpeed = rotSpeed;
    _colStart = colStart;
    _colEnd = colEnd;

    _pts = BuildControlPoints(start, target, curveStrength, cpCount);

    _mat = GetComponent<Renderer>().material;
    SetColor(0f);
  }

  void Update()
  {
    _elapsed += Time.deltaTime;
    float t = Mathf.Clamp01(_elapsed / _duration);

    transform.position = EvaluateBezier(_pts, t);

    // face movement direction
    if (t < 0.999f)
    {
      Vector3 next = EvaluateBezier(_pts, Mathf.Clamp01(t + 0.01f));
      Vector3 dir = next - transform.position;
      if (dir.sqrMagnitude > 0.0001f)
        transform.rotation = Quaternion.LookRotation(dir) *
                             Quaternion.Euler(0f, 0f, _rotSpeed * _elapsed);
    }

    SetColor(t);

    if (t >= 1f) Destroy(gameObject);
  }

  void SetColor(float t)
  {
    Color c = Color.Lerp(_colStart, _colEnd, t);
    // URP/HDRP Lit shader
    if (_mat.HasProperty(ColorID)) _mat.SetColor(ColorID, c);
    else if (_mat.HasProperty("_Color")) _mat.SetColor("_Color", c);
  }

  // --- Bezier helpers ---

  static Vector3[] BuildControlPoints(Vector3 start, Vector3 end,
                                      float strength, int cpCount)
  {
    // pts[0] = start, pts[last] = end, middle = random control points
    var pts = new Vector3[cpCount + 2];
    pts[0] = start;
    pts[pts.Length - 1] = end;

    for (int i = 1; i <= cpCount; i++)
    {
      float ratio = (float)i / (cpCount + 1);
      Vector3 mid = Vector3.Lerp(start, end, ratio);
      pts[i] = mid + Random.insideUnitSphere * strength;
    }
    return pts;
  }

  // De Casteljau โ€” works for any number of control points
  static Vector3 EvaluateBezier(Vector3[] pts, float t)
  {
    Vector3[] tmp = (Vector3[])pts.Clone();
    int n = tmp.Length;
    for (int r = 1; r < n; r++)
      for (int i = 0; i < n - r; i++)
        tmp[i] = Vector3.LerpUnclamped(tmp[i], tmp[i + 1], t);
    return tmp[0];
  }
}

๐Ÿ“„FloatingMotion.cs

using UnityEngine;

// ํšŒ์ „ ์—†์ด Perlin Noise ๊ธฐ๋ฐ˜์œผ๋กœ ์ผ์ • ๋ฒ”์œ„ ๋‚ด๋ฅผ ๋ถ€์œ ํ•˜๋“ฏ ์ด๋™
// Inspector์—์„œ ๋ฒ”์œ„, ์†๋„, ๊ฐ ์ถ• ์˜คํ”„์…‹์„ ์กฐ์ • ๊ฐ€๋Šฅ
public class FloatingMotion : MonoBehaviour
{
    // ===== Settings =====
    [Header("Range")]
    [SerializeField] private Vector3 range = new Vector3(10f, 10f, 10f);

    [Header("Speed")]
    [SerializeField] private float speed = 0.2f;   // Perlin ์ƒ˜ํ”Œ๋ง ์†๋„ (๋‚ฎ์„์ˆ˜๋ก ๋А๋ฆฌ๊ณ  ๋ถ€๋“œ๋Ÿฌ์›€)

    [Header("Noise Offsets")]
    [SerializeField] private Vector3 noiseOffset;  // ํ”„๋ฆฌํŒน๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฐ’์„ ์ฃผ๋ฉด ๋™๊ธฐํ™” ๋ฐฉ์ง€

    // ===== Private =====
    private Vector3 _originPosition;

    // ===== Unity =====
    void Start()
    {
        _originPosition = transform.position;

        // ์˜คํ”„์…‹ ๋ฏธ์„ค์ • ์‹œ ๋žœ๋ค ์ดˆ๊ธฐํ™” (์—ฌ๋Ÿฌ ํ”„๋ฆฌํŒน์ด ๋™๊ธฐํ™”๋˜์ง€ ์•Š๋„๋ก)
        if (noiseOffset == Vector3.zero)
            noiseOffset = new Vector3(
                Random.Range(0f, 100f),
                Random.Range(0f, 100f),
                Random.Range(0f, 100f)
            );
    }

    void Update()
    {
        float t = Time.time * speed;

        // ๊ฐ ์ถ•๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ Perlin ์ƒ˜ํ”Œ โ†’ [0,1] โ†’ [-0.5, 0.5] โ†’ range ์ ์šฉ
        float x = (Mathf.PerlinNoise(t + noiseOffset.x, 0f) - 0.5f) * range.x;
        float y = (Mathf.PerlinNoise(t + noiseOffset.y, 1f) - 0.5f) * range.y;
        float z = (Mathf.PerlinNoise(t + noiseOffset.z, 2f) - 0.5f) * range.z;

        transform.position = _originPosition + new Vector3(x, y, z);
    }
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€