main
parent
203439b94c
commit
ebc4360315
Binary file not shown.
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a54962df60666114982d252f08b68d45
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09b313e0c93f3a54e8753073aee4ffe5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 相机控制器 - 管理相机的位置和视角
|
||||
/// </summary>
|
||||
public class CameraController : MonoBehaviour
|
||||
{
|
||||
[Header("相机设置")]
|
||||
public Transform target;
|
||||
public float distance = 10f;
|
||||
public float height = 5f;
|
||||
public float rotationSpeed = 2f;
|
||||
public float zoomSpeed = 2f;
|
||||
public float minDistance = 2f;
|
||||
public float maxDistance = 50f;
|
||||
|
||||
[Header("视角限制")]
|
||||
public float minVerticalAngle = -80f;
|
||||
public float maxVerticalAngle = 80f;
|
||||
|
||||
private float currentX = 0f;
|
||||
private float currentY = 0f;
|
||||
private Vector3 offset;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
// 如果没有指定目标,尝试找到迫击炮
|
||||
GameObject mortar = GameObject.FindGameObjectWithTag("Mortar");
|
||||
if (mortar != null)
|
||||
{
|
||||
target = mortar.transform;
|
||||
}
|
||||
}
|
||||
|
||||
offset = transform.position - target.position;
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (target == null) return;
|
||||
|
||||
HandleInput();
|
||||
UpdateCameraPosition();
|
||||
}
|
||||
|
||||
void HandleInput()
|
||||
{
|
||||
// 鼠标控制
|
||||
if (Input.GetMouseButton(1)) // 右键拖拽
|
||||
{
|
||||
currentX += Input.GetAxis("Mouse X") * rotationSpeed;
|
||||
currentY -= Input.GetAxis("Mouse Y") * rotationSpeed;
|
||||
currentY = Mathf.Clamp(currentY, minVerticalAngle, maxVerticalAngle);
|
||||
}
|
||||
|
||||
// 滚轮缩放
|
||||
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||||
distance -= scroll * zoomSpeed;
|
||||
distance = Mathf.Clamp(distance, minDistance, maxDistance);
|
||||
}
|
||||
|
||||
void UpdateCameraPosition()
|
||||
{
|
||||
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
|
||||
Vector3 position = target.position + rotation * Vector3.back * distance + Vector3.up * height;
|
||||
|
||||
transform.position = position;
|
||||
transform.LookAt(target.position + Vector3.up * height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置相机目标
|
||||
/// </summary>
|
||||
/// <param name="newTarget">新的目标</param>
|
||||
public void SetTarget(Transform newTarget)
|
||||
{
|
||||
target = newTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置相机位置
|
||||
/// </summary>
|
||||
public void ResetCamera()
|
||||
{
|
||||
currentX = 0f;
|
||||
currentY = 0f;
|
||||
distance = 10f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 145c118935ed67f4c811f367dd30463f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9dd8af9fda7f38498743ed475419e8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,457 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 迫击炮控制器 - 管理迫击炮的发射和动画
|
||||
/// </summary>
|
||||
public class MortarController : MonoBehaviour
|
||||
{
|
||||
[Header("迫击炮设置")]
|
||||
public string mortarCaliber = "82mm";
|
||||
public Transform barrelTransform;
|
||||
public Transform baseTransform;
|
||||
public float barrelLength = 1.2f;
|
||||
public float maxElevation = 80f;
|
||||
public float minElevation = 10f;
|
||||
|
||||
[Header("发射设置")]
|
||||
public GameObject projectilePrefab;
|
||||
public Transform projectileSpawnPoint;
|
||||
public float projectileScale = 1f;
|
||||
public float launchForce = 1000f;
|
||||
|
||||
[Header("动画设置")]
|
||||
public float elevationSpeed = 30f; // 度/秒
|
||||
public float recoilDistance = 0.1f;
|
||||
public float recoilDuration = 0.2f;
|
||||
public AnimationCurve recoilCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||||
|
||||
[Header("音效")]
|
||||
public AudioSource audioSource;
|
||||
public AudioClip fireSound;
|
||||
public AudioClip reloadSound;
|
||||
|
||||
[Header("特效")]
|
||||
public ParticleSystem muzzleFlash;
|
||||
public ParticleSystem smokeEffect;
|
||||
|
||||
// 私有变量
|
||||
private float currentElevation = 45f;
|
||||
private Vector3 originalBarrelPosition;
|
||||
private bool isFiring = false;
|
||||
private BallisticsCalculator ballisticsCalculator;
|
||||
private TrajectoryRenderer trajectoryRenderer;
|
||||
|
||||
// 事件
|
||||
public System.Action<BallisticsCalculator.BallisticsResult> OnTrajectoryCalculated;
|
||||
public System.Action OnFired;
|
||||
|
||||
void Start()
|
||||
{
|
||||
InitializeComponents();
|
||||
SetupMortar();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化组件
|
||||
/// </summary>
|
||||
private void InitializeComponents()
|
||||
{
|
||||
ballisticsCalculator = new BallisticsCalculator();
|
||||
trajectoryRenderer = GetComponent<TrajectoryRenderer>();
|
||||
|
||||
if (trajectoryRenderer == null)
|
||||
{
|
||||
trajectoryRenderer = gameObject.AddComponent<TrajectoryRenderer>();
|
||||
}
|
||||
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
}
|
||||
|
||||
originalBarrelPosition = barrelTransform.localPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置迫击炮
|
||||
/// </summary>
|
||||
private void SetupMortar()
|
||||
{
|
||||
// 设置初始仰角
|
||||
SetElevation(currentElevation);
|
||||
|
||||
// 设置炮管长度
|
||||
if (barrelTransform != null)
|
||||
{
|
||||
barrelTransform.localScale = new Vector3(1f, barrelLength, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置仰角
|
||||
/// </summary>
|
||||
public void SetElevation(float angle)
|
||||
{
|
||||
currentElevation = Mathf.Clamp(angle, minElevation, maxElevation);
|
||||
|
||||
if (barrelTransform != null)
|
||||
{
|
||||
barrelTransform.localRotation = Quaternion.Euler(-currentElevation, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调整仰角
|
||||
/// </summary>
|
||||
public void AdjustElevation(float deltaAngle)
|
||||
{
|
||||
SetElevation(currentElevation + deltaAngle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算并显示轨迹
|
||||
/// </summary>
|
||||
public void CalculateAndShowTrajectory(
|
||||
float targetDistance,
|
||||
float windSpeed = 0f,
|
||||
float windDirection = 0f,
|
||||
float temperature = 15f,
|
||||
float pressure = 1013.25f,
|
||||
float humidity = 50f)
|
||||
{
|
||||
if (isFiring) return;
|
||||
|
||||
// 火控解算
|
||||
var result = ballisticsCalculator.FireControlSolution(
|
||||
mortarCaliber,
|
||||
targetDistance,
|
||||
windSpeed,
|
||||
windDirection,
|
||||
temperature,
|
||||
pressure,
|
||||
humidity
|
||||
);
|
||||
|
||||
if (result.success)
|
||||
{
|
||||
// 更新仰角
|
||||
SetElevation(CalculateOptimalElevation(targetDistance, result.trajectoryPoints));
|
||||
|
||||
// 绘制轨迹
|
||||
Vector3 impactPoint = GetImpactPoint(result.trajectoryPoints);
|
||||
trajectoryRenderer.DrawTrajectory(result.trajectoryPoints, result.maxHeight, impactPoint);
|
||||
|
||||
// 触发事件
|
||||
OnTrajectoryCalculated?.Invoke(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 显示警告
|
||||
Debug.LogWarning(result.warning);
|
||||
if (!string.IsNullOrEmpty(result.recommendation))
|
||||
{
|
||||
Debug.Log(result.recommendation);
|
||||
}
|
||||
|
||||
// 清除轨迹
|
||||
trajectoryRenderer.ClearTrajectory();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算最佳仰角
|
||||
/// </summary>
|
||||
private float CalculateOptimalElevation(float targetDistance, Vector3[] trajectoryPoints)
|
||||
{
|
||||
if (trajectoryPoints == null || trajectoryPoints.Length < 2)
|
||||
return currentElevation;
|
||||
|
||||
// 计算初始速度方向
|
||||
Vector3 initialDirection = (trajectoryPoints[1] - trajectoryPoints[0]).normalized;
|
||||
float angle = Mathf.Atan2(initialDirection.y, initialDirection.x) * Mathf.Rad2Deg;
|
||||
|
||||
return Mathf.Clamp(angle, minElevation, maxElevation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取落点
|
||||
/// </summary>
|
||||
private Vector3 GetImpactPoint(Vector3[] trajectoryPoints)
|
||||
{
|
||||
if (trajectoryPoints == null || trajectoryPoints.Length == 0)
|
||||
return Vector3.zero;
|
||||
|
||||
return trajectoryPoints[trajectoryPoints.Length - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发射弹丸
|
||||
/// </summary>
|
||||
public void FireProjectile()
|
||||
{
|
||||
if (isFiring || projectilePrefab == null) return;
|
||||
|
||||
StartCoroutine(FireSequence());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发射序列
|
||||
/// </summary>
|
||||
private IEnumerator FireSequence()
|
||||
{
|
||||
isFiring = true;
|
||||
|
||||
// 1. 后坐动画
|
||||
yield return StartCoroutine(RecoilAnimation());
|
||||
|
||||
// 2. 创建弹丸
|
||||
CreateProjectile();
|
||||
|
||||
// 3. 播放音效
|
||||
PlayFireSound();
|
||||
|
||||
// 4. 播放特效
|
||||
PlayMuzzleEffects();
|
||||
|
||||
// 5. 等待后坐恢复
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
|
||||
isFiring = false;
|
||||
OnFired?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后坐动画
|
||||
/// </summary>
|
||||
private IEnumerator RecoilAnimation()
|
||||
{
|
||||
Vector3 startPos = barrelTransform.localPosition;
|
||||
Vector3 recoilPos = startPos - barrelTransform.forward * recoilDistance;
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < recoilDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / recoilDuration;
|
||||
float curveValue = recoilCurve.Evaluate(t);
|
||||
|
||||
barrelTransform.localPosition = Vector3.Lerp(startPos, recoilPos, curveValue);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// 恢复原位
|
||||
elapsed = 0f;
|
||||
while (elapsed < recoilDuration)
|
||||
{
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / recoilDuration;
|
||||
|
||||
barrelTransform.localPosition = Vector3.Lerp(recoilPos, startPos, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
barrelTransform.localPosition = startPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建弹丸
|
||||
/// </summary>
|
||||
private void CreateProjectile()
|
||||
{
|
||||
if (projectileSpawnPoint == null)
|
||||
projectileSpawnPoint = barrelTransform;
|
||||
|
||||
GameObject projectile = Instantiate(projectilePrefab, projectileSpawnPoint.position, projectileSpawnPoint.rotation);
|
||||
projectile.transform.localScale = Vector3.one * projectileScale;
|
||||
|
||||
// 添加物理组件
|
||||
Rigidbody rb = projectile.GetComponent<Rigidbody>();
|
||||
if (rb == null)
|
||||
{
|
||||
rb = projectile.AddComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
// 设置初速
|
||||
Vector3 launchDirection = barrelTransform.forward;
|
||||
rb.velocity = launchDirection * (launchForce / 100f); // 调整力度
|
||||
|
||||
// 添加弹丸脚本
|
||||
Projectile projectileScript = projectile.GetComponent<Projectile>();
|
||||
if (projectileScript == null)
|
||||
{
|
||||
projectileScript = projectile.AddComponent<Projectile>();
|
||||
}
|
||||
|
||||
projectileScript.Initialize(mortarCaliber, ballisticsCalculator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放发射音效
|
||||
/// </summary>
|
||||
private void PlayFireSound()
|
||||
{
|
||||
if (audioSource != null && fireSound != null)
|
||||
{
|
||||
audioSource.PlayOneShot(fireSound);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放炮口特效
|
||||
/// </summary>
|
||||
private void PlayMuzzleEffects()
|
||||
{
|
||||
if (muzzleFlash != null)
|
||||
{
|
||||
muzzleFlash.Play();
|
||||
}
|
||||
|
||||
if (smokeEffect != null)
|
||||
{
|
||||
smokeEffect.Play();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除轨迹
|
||||
/// </summary>
|
||||
public void ClearTrajectory()
|
||||
{
|
||||
trajectoryRenderer.ClearTrajectory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置迫击炮型号
|
||||
/// </summary>
|
||||
public void SetMortarCaliber(string caliber)
|
||||
{
|
||||
mortarCaliber = caliber;
|
||||
|
||||
// 根据口径调整炮管长度
|
||||
var mortarData = ballisticsCalculator.GetMortarData(caliber);
|
||||
if (mortarData != null)
|
||||
{
|
||||
barrelLength = mortarData.caliber / 1000f * 20f; // 按比例缩放
|
||||
SetupMortar();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前仰角
|
||||
/// </summary>
|
||||
public float GetCurrentElevation()
|
||||
{
|
||||
return currentElevation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取迫击炮数据
|
||||
/// </summary>
|
||||
public BallisticsCalculator.MortarData GetMortarData()
|
||||
{
|
||||
return ballisticsCalculator.GetMortarData(mortarCaliber);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 键盘控制
|
||||
HandleInput();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理输入
|
||||
/// </summary>
|
||||
private void HandleInput()
|
||||
{
|
||||
if (isFiring) return;
|
||||
|
||||
// 仰角调整
|
||||
if (Input.GetKey(KeyCode.Q))
|
||||
{
|
||||
AdjustElevation(elevationSpeed * Time.deltaTime);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.E))
|
||||
{
|
||||
AdjustElevation(-elevationSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
// 发射
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
FireProjectile();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置迫击炮类型
|
||||
/// </summary>
|
||||
/// <param name="mortarType">迫击炮类型索引</param>
|
||||
public void SetMortarType(int mortarType)
|
||||
{
|
||||
// 根据类型设置不同的迫击炮参数
|
||||
switch (mortarType)
|
||||
{
|
||||
case 0: // 82mm迫击炮
|
||||
mortarCaliber = "82mm";
|
||||
launchForce = 1000f;
|
||||
maxElevation = 80f;
|
||||
minElevation = 10f;
|
||||
break;
|
||||
case 1: // 120mm迫击炮
|
||||
mortarCaliber = "120mm";
|
||||
launchForce = 1500f;
|
||||
maxElevation = 85f;
|
||||
minElevation = 15f;
|
||||
break;
|
||||
case 2: // 60mm迫击炮
|
||||
mortarCaliber = "60mm";
|
||||
launchForce = 800f;
|
||||
maxElevation = 75f;
|
||||
minElevation = 8f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置目标距离
|
||||
/// </summary>
|
||||
/// <param name="distance">目标距离</param>
|
||||
public void SetTargetDistance(float distance)
|
||||
{
|
||||
// 这里可以用于计算合适的仰角
|
||||
// 实际实现可能需要弹道计算
|
||||
Debug.Log($"目标距离设置为: {distance}m");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置仰角
|
||||
/// </summary>
|
||||
/// <param name="angle">仰角(度)</param>
|
||||
public void SetElevationAngle(float angle)
|
||||
{
|
||||
SetElevation(angle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置风力条件
|
||||
/// </summary>
|
||||
/// <param name="speed">风速</param>
|
||||
/// <param name="direction">风向(度)</param>
|
||||
public void SetWindConditions(float speed, float direction)
|
||||
{
|
||||
// 这里可以设置风力参数,影响弹道计算
|
||||
Debug.Log($"风力条件: 风速{speed}m/s, 风向{direction}°");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发射迫击炮
|
||||
/// </summary>
|
||||
public void Fire()
|
||||
{
|
||||
FireProjectile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ee0a456f48ccfa46876cf8ecc0b4405
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cc177faa7f276f4a87bce194cd884a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd31bbcc48259e740b7f6ccf3d5f7039
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3548d19bab946624ca554abb39a35489
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,208 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 简化版场景设置脚本 - 不依赖UI包
|
||||
/// </summary>
|
||||
public class SceneSetup_Simple : MonoBehaviour
|
||||
{
|
||||
[Header("场景设置")]
|
||||
public bool autoSetup = true;
|
||||
public bool createGround = true;
|
||||
public bool createTarget = true;
|
||||
public bool createMortar = true;
|
||||
|
||||
[Header("地面设置")]
|
||||
public Material groundMaterial;
|
||||
public float groundSize = 1000f;
|
||||
|
||||
[Header("目标设置")]
|
||||
public Material targetMaterial;
|
||||
public float targetDistance = 1000f;
|
||||
public float targetHeight = 0f;
|
||||
|
||||
[Header("迫击炮设置")]
|
||||
public Material mortarMaterial;
|
||||
public Vector3 mortarPosition = new Vector3(0, 0, 0);
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (autoSetup)
|
||||
{
|
||||
SetupScene();
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("设置场景")]
|
||||
public void SetupScene()
|
||||
{
|
||||
Debug.Log("开始设置场景...");
|
||||
|
||||
if (createGround)
|
||||
{
|
||||
CreateGround();
|
||||
}
|
||||
|
||||
if (createTarget)
|
||||
{
|
||||
CreateTarget();
|
||||
}
|
||||
|
||||
if (createMortar)
|
||||
{
|
||||
CreateMortar();
|
||||
}
|
||||
|
||||
SetupLighting();
|
||||
SetupCamera();
|
||||
|
||||
Debug.Log("场景设置完成!");
|
||||
}
|
||||
|
||||
void CreateGround()
|
||||
{
|
||||
// 创建地面
|
||||
GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
||||
ground.name = "Ground";
|
||||
ground.transform.position = Vector3.zero;
|
||||
ground.transform.localScale = new Vector3(groundSize / 10f, 1, groundSize / 10f);
|
||||
|
||||
// 设置材质
|
||||
if (groundMaterial != null)
|
||||
{
|
||||
ground.GetComponent<Renderer>().material = groundMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建默认材质
|
||||
Material defaultGroundMat = new Material(Shader.Find("Standard"));
|
||||
defaultGroundMat.color = new Color(0.3f, 0.6f, 0.3f); // 绿色
|
||||
ground.GetComponent<Renderer>().material = defaultGroundMat;
|
||||
}
|
||||
|
||||
// 添加碰撞器
|
||||
if (ground.GetComponent<Collider>() == null)
|
||||
{
|
||||
ground.AddComponent<BoxCollider>();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTarget()
|
||||
{
|
||||
// 创建目标
|
||||
GameObject target = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
target.name = "Target";
|
||||
target.transform.position = new Vector3(targetDistance, targetHeight, 0);
|
||||
target.transform.localScale = new Vector3(2f, 5f, 2f);
|
||||
|
||||
// 设置材质
|
||||
if (targetMaterial != null)
|
||||
{
|
||||
target.GetComponent<Renderer>().material = targetMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建默认材质
|
||||
Material defaultTargetMat = new Material(Shader.Find("Standard"));
|
||||
defaultTargetMat.color = Color.red;
|
||||
target.GetComponent<Renderer>().material = defaultTargetMat;
|
||||
}
|
||||
|
||||
// 添加目标脚本
|
||||
if (target.GetComponent<Target>() == null)
|
||||
{
|
||||
target.AddComponent<Target>();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMortar()
|
||||
{
|
||||
// 创建迫击炮
|
||||
GameObject mortar = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||
mortar.name = "Mortar";
|
||||
mortar.transform.position = mortarPosition;
|
||||
mortar.transform.localScale = new Vector3(0.5f, 0.3f, 0.5f);
|
||||
|
||||
// 设置材质
|
||||
if (mortarMaterial != null)
|
||||
{
|
||||
mortar.GetComponent<Renderer>().material = mortarMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建默认材质
|
||||
Material defaultMortarMat = new Material(Shader.Find("Standard"));
|
||||
defaultMortarMat.color = new Color(0.5f, 0.5f, 0.5f); // 灰色
|
||||
mortar.GetComponent<Renderer>().material = defaultMortarMat;
|
||||
}
|
||||
|
||||
// 添加迫击炮控制器
|
||||
if (mortar.GetComponent<MortarController>() == null)
|
||||
{
|
||||
mortar.AddComponent<MortarController>();
|
||||
}
|
||||
}
|
||||
|
||||
void SetupLighting()
|
||||
{
|
||||
// 设置主光源
|
||||
Light mainLight = FindObjectOfType<Light>();
|
||||
if (mainLight == null)
|
||||
{
|
||||
GameObject lightObj = new GameObject("Main Light");
|
||||
mainLight = lightObj.AddComponent<Light>();
|
||||
}
|
||||
|
||||
mainLight.type = LightType.Directional;
|
||||
mainLight.color = Color.white;
|
||||
mainLight.intensity = 1f;
|
||||
mainLight.transform.rotation = Quaternion.Euler(45f, 45f, 0f);
|
||||
}
|
||||
|
||||
void SetupCamera()
|
||||
{
|
||||
// 设置主摄像机
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null)
|
||||
{
|
||||
GameObject cameraObj = new GameObject("Main Camera");
|
||||
mainCamera = cameraObj.AddComponent<Camera>();
|
||||
cameraObj.tag = "MainCamera";
|
||||
}
|
||||
|
||||
// 设置摄像机位置和角度
|
||||
mainCamera.transform.position = new Vector3(0, 50, -100);
|
||||
mainCamera.transform.LookAt(new Vector3(0, 0, 0));
|
||||
mainCamera.fieldOfView = 60f;
|
||||
|
||||
// 添加摄像机控制脚本
|
||||
if (mainCamera.GetComponent<CameraController>() == null)
|
||||
{
|
||||
mainCamera.gameObject.AddComponent<CameraController>();
|
||||
}
|
||||
}
|
||||
|
||||
// 在编辑器中显示设置按钮
|
||||
[ContextMenu("清理场景")]
|
||||
public void CleanupScene()
|
||||
{
|
||||
// 删除自动创建的对象
|
||||
GameObject[] objectsToDelete = {
|
||||
GameObject.Find("Ground"),
|
||||
GameObject.Find("Target"),
|
||||
GameObject.Find("Mortar")
|
||||
};
|
||||
|
||||
foreach (GameObject obj in objectsToDelete)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("场景已清理");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 520ce9c0cf098cb4b89f6d1d9fc3cfe5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,276 @@
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 简化迫击炮控制器 - 不依赖UI包
|
||||
/// 使用OnGUI进行界面显示
|
||||
/// </summary>
|
||||
public class SimpleMortarController : MonoBehaviour
|
||||
{
|
||||
[Header("迫击炮设置")]
|
||||
public Transform mortarTransform;
|
||||
public GameObject projectilePrefab;
|
||||
public Transform firePoint;
|
||||
|
||||
[Header("弹道参数")]
|
||||
[Range(30f, 80f)]
|
||||
public float elevationAngle = 45f;
|
||||
|
||||
[Range(50f, 200f)]
|
||||
public float muzzleVelocity = 100f;
|
||||
|
||||
[Range(0f, 20f)]
|
||||
public float windSpeed = 5f;
|
||||
|
||||
[Range(0f, 360f)]
|
||||
public float windDirection = 0f;
|
||||
|
||||
[Header("物理参数")]
|
||||
public float gravity = 9.81f;
|
||||
public float airDensity = 1.225f;
|
||||
public float dragCoefficient = 0.47f;
|
||||
|
||||
[Header("显示设置")]
|
||||
public bool showTrajectory = true;
|
||||
public bool showParameters = true;
|
||||
public Color trajectoryColor = Color.red;
|
||||
public int trajectoryPoints = 50;
|
||||
|
||||
private Vector3[] trajectoryPoints_array;
|
||||
private bool isFired = false;
|
||||
private float flightTime = 0f;
|
||||
private Vector3 impactPoint;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (mortarTransform == null)
|
||||
mortarTransform = transform;
|
||||
|
||||
if (firePoint == null)
|
||||
firePoint = transform;
|
||||
|
||||
trajectoryPoints_array = new Vector3[trajectoryPoints];
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 键盘控制
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
Fire();
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.Q))
|
||||
{
|
||||
elevationAngle = Mathf.Clamp(elevationAngle + 1f, 30f, 80f);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.E))
|
||||
{
|
||||
elevationAngle = Mathf.Clamp(elevationAngle - 1f, 30f, 80f);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.W))
|
||||
{
|
||||
muzzleVelocity = Mathf.Clamp(muzzleVelocity + 5f, 50f, 200f);
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.S))
|
||||
{
|
||||
muzzleVelocity = Mathf.Clamp(muzzleVelocity - 5f, 50f, 200f);
|
||||
}
|
||||
|
||||
// 计算弹道
|
||||
if (showTrajectory)
|
||||
{
|
||||
CalculateTrajectory();
|
||||
}
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (!showParameters) return;
|
||||
|
||||
GUILayout.BeginArea(new Rect(10, 10, 300, 400));
|
||||
GUILayout.BeginVertical("box");
|
||||
|
||||
GUILayout.Label("迫击炮控制系统", GUI.skin.box);
|
||||
GUILayout.Space(10);
|
||||
|
||||
// 参数显示
|
||||
GUILayout.Label($"仰角: {elevationAngle:F1}°");
|
||||
GUILayout.Label($"初速: {muzzleVelocity:F1} m/s");
|
||||
GUILayout.Label($"风速: {windSpeed:F1} m/s");
|
||||
GUILayout.Label($"风向: {windDirection:F1}°");
|
||||
GUILayout.Space(10);
|
||||
|
||||
// 控制说明
|
||||
GUILayout.Label("控制说明:");
|
||||
GUILayout.Label("Q/E - 调整仰角");
|
||||
GUILayout.Label("W/S - 调整初速");
|
||||
GUILayout.Label("空格 - 发射");
|
||||
GUILayout.Space(10);
|
||||
|
||||
// 按钮控制
|
||||
if (GUILayout.Button("发射炮弹"))
|
||||
{
|
||||
Fire();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("重置参数"))
|
||||
{
|
||||
ResetParameters();
|
||||
}
|
||||
|
||||
if (GUILayout.Button(showTrajectory ? "隐藏弹道" : "显示弹道"))
|
||||
{
|
||||
showTrajectory = !showTrajectory;
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
// 弹道信息
|
||||
if (isFired)
|
||||
{
|
||||
GUILayout.Label("弹道信息:");
|
||||
GUILayout.Label($"飞行时间: {flightTime:F2}秒");
|
||||
GUILayout.Label($"落点距离: {impactPoint.x:F1}m");
|
||||
GUILayout.Label($"最大高度: {GetMaxHeight():F1}m");
|
||||
}
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
if (!showTrajectory || trajectoryPoints_array == null) return;
|
||||
|
||||
Gizmos.color = trajectoryColor;
|
||||
for (int i = 0; i < trajectoryPoints_array.Length - 1; i++)
|
||||
{
|
||||
if (trajectoryPoints_array[i] != Vector3.zero && trajectoryPoints_array[i + 1] != Vector3.zero)
|
||||
{
|
||||
Gizmos.DrawLine(trajectoryPoints_array[i], trajectoryPoints_array[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制落点
|
||||
if (isFired && impactPoint != Vector3.zero)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(impactPoint, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateTrajectory()
|
||||
{
|
||||
Vector3 startPos = firePoint.position;
|
||||
Vector3 windVector = new Vector3(
|
||||
Mathf.Cos(windDirection * Mathf.Deg2Rad) * windSpeed,
|
||||
0,
|
||||
Mathf.Sin(windDirection * Mathf.Deg2Rad) * windSpeed
|
||||
);
|
||||
|
||||
float angleRad = elevationAngle * Mathf.Deg2Rad;
|
||||
Vector3 initialVelocity = new Vector3(
|
||||
muzzleVelocity * Mathf.Cos(angleRad),
|
||||
muzzleVelocity * Mathf.Sin(angleRad),
|
||||
0
|
||||
);
|
||||
|
||||
float timeStep = 0.1f;
|
||||
float totalTime = 0f;
|
||||
|
||||
for (int i = 0; i < trajectoryPoints; i++)
|
||||
{
|
||||
float t = i * timeStep;
|
||||
Vector3 position = CalculatePositionAtTime(startPos, initialVelocity, windVector, t);
|
||||
trajectoryPoints_array[i] = position;
|
||||
|
||||
// 检查是否落地
|
||||
if (position.y <= startPos.y && i > 0)
|
||||
{
|
||||
totalTime = t;
|
||||
impactPoint = position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 CalculatePositionAtTime(Vector3 startPos, Vector3 initialVel, Vector3 wind, float time)
|
||||
{
|
||||
// 简化的弹道计算(忽略空气阻力)
|
||||
Vector3 position = startPos + initialVel * time + 0.5f * Physics.gravity * time * time;
|
||||
position += wind * time; // 风力影响
|
||||
return position;
|
||||
}
|
||||
|
||||
void Fire()
|
||||
{
|
||||
if (projectilePrefab == null) return;
|
||||
|
||||
// 创建炮弹
|
||||
GameObject projectile = Instantiate(projectilePrefab, firePoint.position, firePoint.rotation);
|
||||
|
||||
// 计算初始速度
|
||||
float angleRad = elevationAngle * Mathf.Deg2Rad;
|
||||
Vector3 initialVelocity = new Vector3(
|
||||
muzzleVelocity * Mathf.Cos(angleRad),
|
||||
muzzleVelocity * Mathf.Sin(angleRad),
|
||||
0
|
||||
);
|
||||
|
||||
// 添加风力
|
||||
Vector3 windVector = new Vector3(
|
||||
Mathf.Cos(windDirection * Mathf.Deg2Rad) * windSpeed,
|
||||
0,
|
||||
Mathf.Sin(windDirection * Mathf.Deg2Rad) * windSpeed
|
||||
);
|
||||
|
||||
initialVelocity += windVector;
|
||||
|
||||
// 应用力
|
||||
Rigidbody rb = projectile.GetComponent<Rigidbody>();
|
||||
if (rb != null)
|
||||
{
|
||||
rb.velocity = initialVelocity;
|
||||
}
|
||||
|
||||
// 计算飞行时间
|
||||
flightTime = 2f * muzzleVelocity * Mathf.Sin(angleRad) / gravity;
|
||||
impactPoint = CalculateImpactPoint();
|
||||
isFired = true;
|
||||
|
||||
Debug.Log($"发射炮弹! 仰角: {elevationAngle}°, 初速: {muzzleVelocity} m/s");
|
||||
}
|
||||
|
||||
Vector3 CalculateImpactPoint()
|
||||
{
|
||||
float angleRad = elevationAngle * Mathf.Deg2Rad;
|
||||
float range = (muzzleVelocity * muzzleVelocity * Mathf.Sin(2f * angleRad)) / gravity;
|
||||
return firePoint.position + new Vector3(range, 0, 0);
|
||||
}
|
||||
|
||||
float GetMaxHeight()
|
||||
{
|
||||
float angleRad = elevationAngle * Mathf.Deg2Rad;
|
||||
return (muzzleVelocity * muzzleVelocity * Mathf.Sin(angleRad) * Mathf.Sin(angleRad)) / (2f * gravity);
|
||||
}
|
||||
|
||||
void ResetParameters()
|
||||
{
|
||||
elevationAngle = 45f;
|
||||
muzzleVelocity = 100f;
|
||||
windSpeed = 5f;
|
||||
windDirection = 0f;
|
||||
isFired = false;
|
||||
flightTime = 0f;
|
||||
impactPoint = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d328df3895edb574eac56a7b52db7a8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,88 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 简化炮弹脚本 - 不依赖UI包
|
||||
/// </summary>
|
||||
public class SimpleProjectile : MonoBehaviour
|
||||
{
|
||||
[Header("炮弹设置")]
|
||||
public float lifetime = 10f;
|
||||
public GameObject explosionPrefab;
|
||||
public LayerMask groundLayer = 1;
|
||||
|
||||
[Header("视觉效果")]
|
||||
public TrailRenderer trail;
|
||||
public ParticleSystem smokeEffect;
|
||||
|
||||
private bool hasExploded = false;
|
||||
private float startTime;
|
||||
|
||||
void Start()
|
||||
{
|
||||
startTime = Time.time;
|
||||
|
||||
// 自动添加轨迹效果
|
||||
if (trail == null)
|
||||
{
|
||||
trail = gameObject.AddComponent<TrailRenderer>();
|
||||
trail.material = new Material(Shader.Find("Sprites/Default"));
|
||||
trail.startColor = Color.red;
|
||||
trail.endColor = Color.red;
|
||||
trail.startWidth = 0.1f;
|
||||
trail.endWidth = 0.01f;
|
||||
trail.time = 2f;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 检查生命周期
|
||||
if (Time.time - startTime > lifetime)
|
||||
{
|
||||
Explode();
|
||||
}
|
||||
|
||||
// 检查是否落地
|
||||
if (transform.position.y < 0.1f && !hasExploded)
|
||||
{
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
|
||||
void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
if (!hasExploded)
|
||||
{
|
||||
Explode();
|
||||
}
|
||||
}
|
||||
|
||||
void Explode()
|
||||
{
|
||||
if (hasExploded) return;
|
||||
|
||||
hasExploded = true;
|
||||
|
||||
// 创建爆炸效果
|
||||
if (explosionPrefab != null)
|
||||
{
|
||||
Instantiate(explosionPrefab, transform.position, Quaternion.identity);
|
||||
}
|
||||
|
||||
// 显示爆炸信息
|
||||
Debug.Log($"炮弹在 {transform.position} 爆炸!");
|
||||
|
||||
// 销毁炮弹
|
||||
Destroy(gameObject, 0.1f);
|
||||
}
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
// 绘制炮弹轨迹
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.DrawWireSphere(transform.position, 0.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cc912c5e4004384aa29537a819aee84
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,206 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 简化场景设置 - 不依赖UI包
|
||||
/// 自动创建基本场景元素
|
||||
/// </summary>
|
||||
public class SimpleSceneSetup : MonoBehaviour
|
||||
{
|
||||
[Header("场景设置")]
|
||||
public bool autoSetupOnStart = true;
|
||||
public bool createGround = true;
|
||||
public bool createTargets = true;
|
||||
public bool createMortar = true;
|
||||
|
||||
[Header("地面设置")]
|
||||
public Material groundMaterial;
|
||||
public Vector3 groundSize = new Vector3(100f, 1f, 100f);
|
||||
|
||||
[Header("目标设置")]
|
||||
public GameObject targetPrefab;
|
||||
public int targetCount = 5;
|
||||
public float targetRange = 50f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (autoSetupOnStart)
|
||||
{
|
||||
SetupScene();
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("设置场景")]
|
||||
public void SetupScene()
|
||||
{
|
||||
Debug.Log("开始设置场景...");
|
||||
|
||||
if (createGround)
|
||||
{
|
||||
CreateGround();
|
||||
}
|
||||
|
||||
if (createTargets)
|
||||
{
|
||||
CreateTargets();
|
||||
}
|
||||
|
||||
if (createMortar)
|
||||
{
|
||||
CreateMortar();
|
||||
}
|
||||
|
||||
Debug.Log("场景设置完成!");
|
||||
}
|
||||
|
||||
void CreateGround()
|
||||
{
|
||||
// 创建地面
|
||||
GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
ground.name = "Ground";
|
||||
ground.transform.position = Vector3.zero;
|
||||
ground.transform.localScale = groundSize;
|
||||
|
||||
// 设置地面材质
|
||||
if (groundMaterial != null)
|
||||
{
|
||||
ground.GetComponent<Renderer>().material = groundMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建简单材质
|
||||
Material mat = new Material(Shader.Find("Standard"));
|
||||
mat.color = new Color(0.4f, 0.6f, 0.2f); // 绿色
|
||||
ground.GetComponent<Renderer>().material = mat;
|
||||
}
|
||||
|
||||
// 添加碰撞器
|
||||
if (ground.GetComponent<Collider>() == null)
|
||||
{
|
||||
ground.AddComponent<BoxCollider>();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTargets()
|
||||
{
|
||||
for (int i = 0; i < targetCount; i++)
|
||||
{
|
||||
GameObject target;
|
||||
|
||||
if (targetPrefab != null)
|
||||
{
|
||||
target = Instantiate(targetPrefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 创建简单目标
|
||||
target = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||
target.name = $"Target_{i + 1}";
|
||||
|
||||
// 设置目标颜色
|
||||
Material targetMat = new Material(Shader.Find("Standard"));
|
||||
targetMat.color = Color.red;
|
||||
target.GetComponent<Renderer>().material = targetMat;
|
||||
}
|
||||
|
||||
// 随机位置
|
||||
float angle = Random.Range(0f, 360f) * Mathf.Deg2Rad;
|
||||
float distance = Random.Range(20f, targetRange);
|
||||
Vector3 position = new Vector3(
|
||||
Mathf.Cos(angle) * distance,
|
||||
1f,
|
||||
Mathf.Sin(angle) * distance
|
||||
);
|
||||
|
||||
target.transform.position = position;
|
||||
target.transform.localScale = new Vector3(2f, 2f, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMortar()
|
||||
{
|
||||
// 创建迫击炮
|
||||
GameObject mortar = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||
mortar.name = "Mortar";
|
||||
mortar.transform.position = new Vector3(0, 0.5f, 0);
|
||||
mortar.transform.localScale = new Vector3(1f, 1f, 1f);
|
||||
|
||||
// 设置迫击炮材质
|
||||
Material mortarMat = new Material(Shader.Find("Standard"));
|
||||
mortarMat.color = new Color(0.3f, 0.3f, 0.3f); // 深灰色
|
||||
mortar.GetComponent<Renderer>().material = mortarMat;
|
||||
|
||||
// 添加迫击炮控制器
|
||||
SimpleMortarController controller = mortar.AddComponent<SimpleMortarController>();
|
||||
controller.mortarTransform = mortar.transform;
|
||||
controller.firePoint = mortar.transform;
|
||||
|
||||
// 创建炮弹预制体
|
||||
GameObject projectilePrefab = CreateProjectilePrefab();
|
||||
controller.projectilePrefab = projectilePrefab;
|
||||
}
|
||||
|
||||
GameObject CreateProjectilePrefab()
|
||||
{
|
||||
// 创建炮弹预制体
|
||||
GameObject projectile = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
projectile.name = "Projectile";
|
||||
projectile.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
|
||||
|
||||
// 设置炮弹材质
|
||||
Material projectileMat = new Material(Shader.Find("Standard"));
|
||||
projectileMat.color = Color.black;
|
||||
projectile.GetComponent<Renderer>().material = projectileMat;
|
||||
|
||||
// 添加物理组件
|
||||
Rigidbody rb = projectile.AddComponent<Rigidbody>();
|
||||
rb.mass = 1f;
|
||||
rb.drag = 0.1f;
|
||||
|
||||
// 添加炮弹脚本
|
||||
projectile.AddComponent<SimpleProjectile>();
|
||||
|
||||
// 添加碰撞器
|
||||
if (projectile.GetComponent<Collider>() == null)
|
||||
{
|
||||
projectile.AddComponent<SphereCollider>();
|
||||
}
|
||||
|
||||
// 设置为预制体
|
||||
projectile.SetActive(false);
|
||||
|
||||
return projectile;
|
||||
}
|
||||
|
||||
[ContextMenu("清理场景")]
|
||||
public void CleanupScene()
|
||||
{
|
||||
// 清理创建的对象
|
||||
GameObject[] objectsToDestroy = {
|
||||
GameObject.Find("Ground"),
|
||||
GameObject.Find("Mortar")
|
||||
};
|
||||
|
||||
foreach (GameObject obj in objectsToDestroy)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理目标
|
||||
GameObject[] targets = GameObject.FindGameObjectsWithTag("Target");
|
||||
foreach (GameObject target in targets)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
DestroyImmediate(target);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("场景清理完成!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3478c69de6b891445bf4194499ba2bba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,86 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 目标类 - 表示迫击炮射击的目标
|
||||
/// </summary>
|
||||
public class Target : MonoBehaviour
|
||||
{
|
||||
[Header("目标设置")]
|
||||
public float health = 100f;
|
||||
public bool isDestroyed = false;
|
||||
public Vector3 targetPosition;
|
||||
|
||||
[Header("视觉效果")]
|
||||
public GameObject explosionEffect;
|
||||
public AudioClip hitSound;
|
||||
public AudioClip destroySound;
|
||||
|
||||
private AudioSource audioSource;
|
||||
|
||||
void Start()
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
if (audioSource == null)
|
||||
{
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
}
|
||||
|
||||
targetPosition = transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 被击中时调用
|
||||
/// </summary>
|
||||
/// <param name="damage">伤害值</param>
|
||||
public void TakeDamage(float damage)
|
||||
{
|
||||
if (isDestroyed) return;
|
||||
|
||||
health -= damage;
|
||||
|
||||
if (audioSource && hitSound)
|
||||
{
|
||||
audioSource.PlayOneShot(hitSound);
|
||||
}
|
||||
|
||||
if (health <= 0)
|
||||
{
|
||||
DestroyTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 摧毁目标
|
||||
/// </summary>
|
||||
public void DestroyTarget()
|
||||
{
|
||||
if (isDestroyed) return;
|
||||
|
||||
isDestroyed = true;
|
||||
|
||||
if (audioSource && destroySound)
|
||||
{
|
||||
audioSource.PlayOneShot(destroySound);
|
||||
}
|
||||
|
||||
if (explosionEffect)
|
||||
{
|
||||
Instantiate(explosionEffect, transform.position, transform.rotation);
|
||||
}
|
||||
|
||||
// 可以在这里添加其他摧毁效果
|
||||
Debug.Log("目标被摧毁!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置目标状态
|
||||
/// </summary>
|
||||
public void ResetTarget()
|
||||
{
|
||||
health = 100f;
|
||||
isDestroyed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9695867a1d8ef254e931b3cb5aeede36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,293 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MortarSimulation
|
||||
{
|
||||
/// <summary>
|
||||
/// 轨迹渲染器 - 负责在3D场景中绘制弹道轨迹
|
||||
/// </summary>
|
||||
public class TrajectoryRenderer : MonoBehaviour
|
||||
{
|
||||
[Header("轨迹设置")]
|
||||
public LineRenderer trajectoryLine;
|
||||
public Material trajectoryMaterial;
|
||||
public float lineWidth = 0.1f;
|
||||
public int maxPoints = 1000;
|
||||
|
||||
[Header("轨迹样式")]
|
||||
public Gradient trajectoryGradient;
|
||||
public AnimationCurve widthCurve = AnimationCurve.Linear(0, 1, 1, 0.1f);
|
||||
|
||||
[Header("标记点")]
|
||||
public GameObject impactMarkerPrefab;
|
||||
public GameObject maxHeightMarkerPrefab;
|
||||
public GameObject trajectoryPointPrefab;
|
||||
|
||||
private GameObject impactMarker;
|
||||
private GameObject maxHeightMarker;
|
||||
private Transform trajectoryPointsParent;
|
||||
|
||||
void Start()
|
||||
{
|
||||
InitializeTrajectoryLine();
|
||||
CreateMarkers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化轨迹线
|
||||
/// </summary>
|
||||
private void InitializeTrajectoryLine()
|
||||
{
|
||||
if (trajectoryLine == null)
|
||||
{
|
||||
trajectoryLine = gameObject.AddComponent<LineRenderer>();
|
||||
}
|
||||
|
||||
trajectoryLine.material = trajectoryMaterial;
|
||||
trajectoryLine.startColor = Color.yellow;
|
||||
trajectoryLine.endColor = Color.yellow;
|
||||
trajectoryLine.startWidth = lineWidth;
|
||||
trajectoryLine.endWidth = lineWidth * 0.1f;
|
||||
trajectoryLine.positionCount = 0;
|
||||
trajectoryLine.useWorldSpace = true;
|
||||
trajectoryLine.sortingOrder = 1;
|
||||
|
||||
// 设置渐变
|
||||
if (trajectoryGradient.colorKeys.Length == 0)
|
||||
{
|
||||
CreateDefaultGradient();
|
||||
}
|
||||
trajectoryLine.colorGradient = trajectoryGradient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认渐变
|
||||
/// </summary>
|
||||
private void CreateDefaultGradient()
|
||||
{
|
||||
trajectoryGradient = new Gradient();
|
||||
trajectoryGradient.SetKeys(
|
||||
new GradientColorKey[] {
|
||||
new GradientColorKey(Color.red, 0.0f),
|
||||
new GradientColorKey(Color.yellow, 0.5f),
|
||||
new GradientColorKey(Color.green, 1.0f)
|
||||
},
|
||||
new GradientAlphaKey[] {
|
||||
new GradientAlphaKey(1.0f, 0.0f),
|
||||
new GradientAlphaKey(0.8f, 0.5f),
|
||||
new GradientAlphaKey(0.0f, 1.0f)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建标记点
|
||||
/// </summary>
|
||||
private void CreateMarkers()
|
||||
{
|
||||
// 创建轨迹点父对象
|
||||
trajectoryPointsParent = new GameObject("TrajectoryPoints").transform;
|
||||
trajectoryPointsParent.SetParent(transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制轨迹
|
||||
/// </summary>
|
||||
/// <param name="trajectoryPoints">轨迹点数组</param>
|
||||
/// <param name="maxHeight">最大高度</param>
|
||||
/// <param name="impactPoint">落点</param>
|
||||
public void DrawTrajectory(Vector3[] trajectoryPoints, float maxHeight, Vector3 impactPoint)
|
||||
{
|
||||
if (trajectoryPoints == null || trajectoryPoints.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("轨迹点数据为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置轨迹线
|
||||
trajectoryLine.positionCount = trajectoryPoints.Length;
|
||||
trajectoryLine.SetPositions(trajectoryPoints);
|
||||
|
||||
// 应用宽度曲线
|
||||
ApplyWidthCurve();
|
||||
|
||||
// 创建标记点
|
||||
CreateTrajectoryMarkers(trajectoryPoints, maxHeight, impactPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用宽度曲线
|
||||
/// </summary>
|
||||
private void ApplyWidthCurve()
|
||||
{
|
||||
if (widthCurve == null || trajectoryLine.positionCount == 0)
|
||||
return;
|
||||
|
||||
AnimationCurve curve = new AnimationCurve();
|
||||
for (int i = 0; i < trajectoryLine.positionCount; i++)
|
||||
{
|
||||
float time = (float)i / (trajectoryLine.positionCount - 1);
|
||||
float width = widthCurve.Evaluate(time) * lineWidth;
|
||||
curve.AddKey(time, width);
|
||||
}
|
||||
|
||||
trajectoryLine.widthCurve = curve;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建轨迹标记点
|
||||
/// </summary>
|
||||
private void CreateTrajectoryMarkers(Vector3[] trajectoryPoints, float maxHeight, Vector3 impactPoint)
|
||||
{
|
||||
// 清除现有标记
|
||||
ClearMarkers();
|
||||
|
||||
// 创建落点标记
|
||||
if (impactMarkerPrefab != null)
|
||||
{
|
||||
impactMarker = Instantiate(impactMarkerPrefab, impactPoint, Quaternion.identity);
|
||||
impactMarker.name = "ImpactMarker";
|
||||
impactMarker.transform.SetParent(trajectoryPointsParent);
|
||||
}
|
||||
|
||||
// 创建最大高度标记
|
||||
if (maxHeightMarkerPrefab != null)
|
||||
{
|
||||
Vector3 maxHeightPoint = FindMaxHeightPoint(trajectoryPoints, maxHeight);
|
||||
maxHeightMarker = Instantiate(maxHeightMarkerPrefab, maxHeightPoint, Quaternion.identity);
|
||||
maxHeightMarker.name = "MaxHeightMarker";
|
||||
maxHeightMarker.transform.SetParent(trajectoryPointsParent);
|
||||
}
|
||||
|
||||
// 创建轨迹点标记(可选)
|
||||
if (trajectoryPointPrefab != null)
|
||||
{
|
||||
CreateTrajectoryPointMarkers(trajectoryPoints);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找最大高度点
|
||||
/// </summary>
|
||||
private Vector3 FindMaxHeightPoint(Vector3[] points, float maxHeight)
|
||||
{
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
if (Mathf.Abs(points[i].y - maxHeight) < 0.1f)
|
||||
{
|
||||
return points[i];
|
||||
}
|
||||
}
|
||||
return points[0]; // 如果没找到,返回第一个点
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建轨迹点标记
|
||||
/// </summary>
|
||||
private void CreateTrajectoryPointMarkers(Vector3[] trajectoryPoints)
|
||||
{
|
||||
int step = Mathf.Max(1, trajectoryPoints.Length / 20); // 最多20个点标记
|
||||
|
||||
for (int i = 0; i < trajectoryPoints.Length; i += step)
|
||||
{
|
||||
GameObject pointMarker = Instantiate(trajectoryPointPrefab, trajectoryPoints[i], Quaternion.identity);
|
||||
pointMarker.name = $"TrajectoryPoint_{i}";
|
||||
pointMarker.transform.SetParent(trajectoryPointsParent);
|
||||
|
||||
// 设置标记大小
|
||||
float scale = Mathf.Lerp(1f, 0.1f, (float)i / trajectoryPoints.Length);
|
||||
pointMarker.transform.localScale = Vector3.one * scale;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有标记
|
||||
/// </summary>
|
||||
public void ClearMarkers()
|
||||
{
|
||||
if (impactMarker != null)
|
||||
{
|
||||
DestroyImmediate(impactMarker);
|
||||
impactMarker = null;
|
||||
}
|
||||
|
||||
if (maxHeightMarker != null)
|
||||
{
|
||||
DestroyImmediate(maxHeightMarker);
|
||||
maxHeightMarker = null;
|
||||
}
|
||||
|
||||
if (trajectoryPointsParent != null)
|
||||
{
|
||||
for (int i = trajectoryPointsParent.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
DestroyImmediate(trajectoryPointsParent.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除轨迹
|
||||
/// </summary>
|
||||
public void ClearTrajectory()
|
||||
{
|
||||
if (trajectoryLine != null)
|
||||
{
|
||||
trajectoryLine.positionCount = 0;
|
||||
}
|
||||
|
||||
ClearMarkers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置轨迹可见性
|
||||
/// </summary>
|
||||
public void SetTrajectoryVisible(bool visible)
|
||||
{
|
||||
if (trajectoryLine != null)
|
||||
{
|
||||
trajectoryLine.enabled = visible;
|
||||
}
|
||||
|
||||
if (trajectoryPointsParent != null)
|
||||
{
|
||||
trajectoryPointsParent.gameObject.SetActive(visible);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新轨迹材质
|
||||
/// </summary>
|
||||
public void UpdateTrajectoryMaterial(Material newMaterial)
|
||||
{
|
||||
if (trajectoryLine != null && newMaterial != null)
|
||||
{
|
||||
trajectoryLine.material = newMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置轨迹颜色
|
||||
/// </summary>
|
||||
public void SetTrajectoryColor(Color color)
|
||||
{
|
||||
if (trajectoryLine != null)
|
||||
{
|
||||
trajectoryLine.startColor = color;
|
||||
trajectoryLine.endColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置轨迹宽度
|
||||
/// </summary>
|
||||
public void SetTrajectoryWidth(float width)
|
||||
{
|
||||
lineWidth = width;
|
||||
if (trajectoryLine != null)
|
||||
{
|
||||
trajectoryLine.startWidth = width;
|
||||
trajectoryLine.endWidth = width * 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5375054b664f70e46af88b133e96aadb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97d9d6ff50de7e342922e8ef983382ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9d7b551021071b439d6af1655c234b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 847cd8b4a5919364f9b27a2ce85288f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue