- 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>
64 lines
2.7 KiB
C#
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;
|
|
}
|
|
}
|