Files
pelpanagiotisandClaude Opus 5.5 5fc9ebf1e6 Add practice range: shooting, AK47, intro cinematic, dressed warehouse scene
- PortalTestDestination practice range: Valorant-style hitscan rifle (recoil,
  spray/bloom, reload, controller support), debug HUD, practice-dummy targets
  with head/body hitboxes, hit/death/respawn animations, procedural SFX/FX
- AK47 model (Quaternius, CC0) held via Animator IK, with fire/reload/equip anims
- Intro cinematic (camera shots, title card, target beam-in, GO hand-off)
- Scene dressing editor pass: CC0 textures, props, warehouse roof, lighting and
  post-processing; custom stylized skybox shader
- CharacterMovement: camera recoil, shoulder offset, cinematic control lock
- Unity CLI pipeline package; plus in-progress chat/Rive/player changes

Asset credits: Assets/Models/Props/PROPS_CREDITS.txt (3 CC-BY 3.0 props),
Assets/Textures/Range/TEXTURES_LICENSE.txt, Assets/Resources/*/..._LICENSE.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 08:56:35 +03:00

64 lines
2.7 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
// Auto-installs the shooting prototype (targets + HitscanShooter) whenever the
// PortalTestDestination scene loads, so no scene/prefab edits are needed. If a
// ShootingRange is already placed in the scene by hand, the bootstrap does nothing.
public class ShootingRange : MonoBehaviour
{
const string SceneName = "PortalTestDestination";
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void RegisterBootstrap()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
}
static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.name != SceneName) return;
if (FindAnyObjectByType<ShootingRange>() != null) return;
var go = new GameObject("ShootingRange");
SceneManager.MoveGameObjectToScene(go, scene);
go.AddComponent<ShootingRange>();
}
void Awake()
{
if (GetComponent<HitscanShooter>() == null)
gameObject.AddComponent<HitscanShooter>();
var targets = SpawnTargets();
gameObject.AddComponent<RangeIntroCinematic>().Begin(targets);
}
// Positions match the greybox map from SetupPortalTestDestinationScene: central street
// along Z (spawns at z = ±14), buildings at x = ±13, crates at (-2,-4) and (2,4).
List<ShootingTarget> SpawnTargets()
{
var root = new GameObject("Targets").transform;
root.SetParent(transform, false);
var list = new List<ShootingTarget>();
// Static bots along the street, visible from either spawn.
list.Add(ShootingTarget.Create(root, "Target_Street_1", new Vector3(0f, 0f, 1.5f)));
list.Add(ShootingTarget.Create(root, "Target_Street_2", new Vector3(0f, 0f, -1.5f)));
list.Add(ShootingTarget.Create(root, "Target_Alley_L1", new Vector3(-6f, 0f, 8f)));
list.Add(ShootingTarget.Create(root, "Target_Alley_R1", new Vector3(6f, 0f, 8f)));
list.Add(ShootingTarget.Create(root, "Target_Alley_L2", new Vector3(-6f, 0f, -8f)));
list.Add(ShootingTarget.Create(root, "Target_Alley_R2", new Vector3(6f, 0f, -8f)));
// Inside the middle buildings (visible through the street-facing doors).
list.Add(ShootingTarget.Create(root, "Target_Building_L", new Vector3(-13f, 0f, 0f)));
list.Add(ShootingTarget.Create(root, "Target_Building_R", new Vector3(13f, 0f, 0f)));
// Strafing bots for tracking practice.
list.Add(ShootingTarget.Create(root, "Target_Strafe_N", new Vector3(0f, 0f, 7f), strafeDistance: 3f));
list.Add(ShootingTarget.Create(root, "Target_Strafe_S", new Vector3(0f, 0f, -7f), strafeDistance: 3f));
return list;
}
}