Add target positions for asteroid orbit and movement
Adds configurable target positions that asteroids can reference: - Orbit mode: asteroid maintains fixed distance to target via constraint - MoveToward mode: asteroid velocity redirected toward target Includes level editor UI for managing targets and asteroid assignments. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
71bb2b25da
commit
496bb50095
@ -1,12 +1,25 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { AsteroidConfig } from '../../levels/config/levelConfig';
|
import type { AsteroidConfig, TargetConfig } from '../../levels/config/levelConfig';
|
||||||
import Button from '../shared/Button.svelte';
|
import Button from '../shared/Button.svelte';
|
||||||
import Section from '../shared/Section.svelte';
|
import Section from '../shared/Section.svelte';
|
||||||
import Vector3Input from './Vector3Input.svelte';
|
import Vector3Input from './Vector3Input.svelte';
|
||||||
import NumberInput from '../shared/NumberInput.svelte';
|
import NumberInput from '../shared/NumberInput.svelte';
|
||||||
import FormGroup from '../shared/FormGroup.svelte';
|
import FormGroup from '../shared/FormGroup.svelte';
|
||||||
|
import Select from '../shared/Select.svelte';
|
||||||
|
|
||||||
export let asteroids: AsteroidConfig[] = [];
|
export let asteroids: AsteroidConfig[] = [];
|
||||||
|
export let targets: TargetConfig[] = [];
|
||||||
|
|
||||||
|
$: targetOptions = [
|
||||||
|
{ value: '', label: '(None)' },
|
||||||
|
...targets.map(t => ({ value: t.id, label: t.name }))
|
||||||
|
];
|
||||||
|
|
||||||
|
const modeOptions = [
|
||||||
|
{ value: '', label: '(None)' },
|
||||||
|
{ value: 'orbit', label: 'Orbit' },
|
||||||
|
{ value: 'moveToward', label: 'Move Toward' }
|
||||||
|
];
|
||||||
|
|
||||||
let editingIndex: number | null = null;
|
let editingIndex: number | null = null;
|
||||||
let editingAsteroid: AsteroidConfig | null = null;
|
let editingAsteroid: AsteroidConfig | null = null;
|
||||||
@ -76,6 +89,14 @@
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
<Vector3Input label="Linear Velocity" bind:value={editingAsteroid.linearVelocity} step={1} />
|
<Vector3Input label="Linear Velocity" bind:value={editingAsteroid.linearVelocity} step={1} />
|
||||||
<Vector3Input label="Angular Velocity" bind:value={editingAsteroid.angularVelocity} step={0.1} />
|
<Vector3Input label="Angular Velocity" bind:value={editingAsteroid.angularVelocity} step={0.1} />
|
||||||
|
<FormGroup label="Target">
|
||||||
|
<Select bind:value={editingAsteroid.targetId} options={targetOptions} />
|
||||||
|
</FormGroup>
|
||||||
|
{#if editingAsteroid.targetId}
|
||||||
|
<FormGroup label="Target Mode">
|
||||||
|
<Select bind:value={editingAsteroid.targetMode} options={modeOptions} />
|
||||||
|
</FormGroup>
|
||||||
|
{/if}
|
||||||
<div class="edit-actions">
|
<div class="edit-actions">
|
||||||
<Button variant="primary" on:click={handleSave}>Save</Button>
|
<Button variant="primary" on:click={handleSave}>Save</Button>
|
||||||
<Button variant="secondary" on:click={handleCancel}>Cancel</Button>
|
<Button variant="secondary" on:click={handleCancel}>Cancel</Button>
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
import StarfieldConfigEditor from './StarfieldConfigEditor.svelte';
|
import StarfieldConfigEditor from './StarfieldConfigEditor.svelte';
|
||||||
import AsteroidListEditor from './AsteroidListEditor.svelte';
|
import AsteroidListEditor from './AsteroidListEditor.svelte';
|
||||||
import PlanetListEditor from './PlanetListEditor.svelte';
|
import PlanetListEditor from './PlanetListEditor.svelte';
|
||||||
|
import TargetListEditor from './TargetListEditor.svelte';
|
||||||
|
|
||||||
export let levelId: string = '';
|
export let levelId: string = '';
|
||||||
|
|
||||||
@ -33,6 +34,7 @@
|
|||||||
{ id: 'base', label: '🛬 Base' },
|
{ id: 'base', label: '🛬 Base' },
|
||||||
{ id: 'sun', label: '☀️ Sun' },
|
{ id: 'sun', label: '☀️ Sun' },
|
||||||
{ id: 'starfield', label: '✨ Stars' },
|
{ id: 'starfield', label: '✨ Stars' },
|
||||||
|
{ id: 'targets', label: '🎯 Targets' },
|
||||||
{ id: 'asteroids', label: '☄️ Asteroids' },
|
{ id: 'asteroids', label: '☄️ Asteroids' },
|
||||||
{ id: 'planets', label: '🪐 Planets' }
|
{ id: 'planets', label: '🪐 Planets' }
|
||||||
];
|
];
|
||||||
@ -124,6 +126,12 @@
|
|||||||
config.starfield = undefined;
|
config.starfield = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureTargets() {
|
||||||
|
if (config && !config.targets) {
|
||||||
|
config.targets = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="editor-container">
|
<div class="editor-container">
|
||||||
@ -170,8 +178,12 @@
|
|||||||
<SunConfigEditor bind:config={config.sun} />
|
<SunConfigEditor bind:config={config.sun} />
|
||||||
{:else if activeTab === 'starfield'}
|
{:else if activeTab === 'starfield'}
|
||||||
<StarfieldConfigEditor config={config.starfield} onToggle={handleStarfieldToggle} />
|
<StarfieldConfigEditor config={config.starfield} onToggle={handleStarfieldToggle} />
|
||||||
|
{:else if activeTab === 'targets'}
|
||||||
|
{ensureTargets()}
|
||||||
|
<TargetListEditor bind:targets={config.targets} />
|
||||||
{:else if activeTab === 'asteroids'}
|
{:else if activeTab === 'asteroids'}
|
||||||
<AsteroidListEditor bind:asteroids={config.asteroids} />
|
{ensureTargets()}
|
||||||
|
<AsteroidListEditor bind:asteroids={config.asteroids} targets={config.targets || []} />
|
||||||
{:else if activeTab === 'planets'}
|
{:else if activeTab === 'planets'}
|
||||||
<PlanetListEditor bind:planets={config.planets} />
|
<PlanetListEditor bind:planets={config.planets} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
185
src/components/editor/TargetListEditor.svelte
Normal file
185
src/components/editor/TargetListEditor.svelte
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { TargetConfig } from '../../levels/config/levelConfig';
|
||||||
|
import Button from '../shared/Button.svelte';
|
||||||
|
import Section from '../shared/Section.svelte';
|
||||||
|
import Vector3Input from './Vector3Input.svelte';
|
||||||
|
import FormGroup from '../shared/FormGroup.svelte';
|
||||||
|
|
||||||
|
export let targets: TargetConfig[] = [];
|
||||||
|
|
||||||
|
let editingIndex: number | null = null;
|
||||||
|
let editingTarget: TargetConfig | null = null;
|
||||||
|
|
||||||
|
function generateId(): string {
|
||||||
|
return `target-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAdd() {
|
||||||
|
const newTarget: TargetConfig = {
|
||||||
|
id: generateId(),
|
||||||
|
name: `Target ${targets.length + 1}`,
|
||||||
|
position: [0, 0, 0]
|
||||||
|
};
|
||||||
|
targets = [...targets, newTarget];
|
||||||
|
editingIndex = targets.length - 1;
|
||||||
|
editingTarget = { ...newTarget };
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(index: number) {
|
||||||
|
editingIndex = index;
|
||||||
|
editingTarget = { ...targets[index] };
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
if (editingIndex !== null && editingTarget) {
|
||||||
|
targets[editingIndex] = editingTarget;
|
||||||
|
targets = targets;
|
||||||
|
}
|
||||||
|
editingIndex = null;
|
||||||
|
editingTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
editingIndex = null;
|
||||||
|
editingTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(index: number) {
|
||||||
|
if (confirm('Delete this target? Asteroids referencing it will lose their target.')) {
|
||||||
|
targets = targets.filter((_, i) => i !== index);
|
||||||
|
if (editingIndex === index) {
|
||||||
|
editingIndex = null;
|
||||||
|
editingTarget = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPosition(pos: [number, number, number]): string {
|
||||||
|
return `${pos[0]}, ${pos[1]}, ${pos[2]}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Section title="Target Positions ({targets.length})">
|
||||||
|
<p class="hint">Targets are positions that asteroids can orbit or move toward.</p>
|
||||||
|
|
||||||
|
<div class="target-header">
|
||||||
|
<Button variant="primary" on:click={handleAdd}>+ Add Target</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if editingTarget !== null}
|
||||||
|
<div class="edit-form">
|
||||||
|
<h4>Edit Target: {editingTarget.name}</h4>
|
||||||
|
<FormGroup label="ID (read-only)">
|
||||||
|
<input type="text" class="settings-input" value={editingTarget.id} disabled />
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup label="Name">
|
||||||
|
<input type="text" class="settings-input" bind:value={editingTarget.name} />
|
||||||
|
</FormGroup>
|
||||||
|
<Vector3Input label="Position" bind:value={editingTarget.position} step={50} />
|
||||||
|
<div class="edit-actions">
|
||||||
|
<Button variant="primary" on:click={handleSave}>Save</Button>
|
||||||
|
<Button variant="secondary" on:click={handleCancel}>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if targets.length === 0}
|
||||||
|
<p class="empty-message">No targets configured. Add targets for asteroids to orbit or move toward.</p>
|
||||||
|
{:else}
|
||||||
|
<div class="target-table">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Position</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each targets as target, index}
|
||||||
|
<tr class:editing={editingIndex === index}>
|
||||||
|
<td>{target.name}</td>
|
||||||
|
<td class="id-cell">{target.id}</td>
|
||||||
|
<td>{formatPosition(target.position)}</td>
|
||||||
|
<td class="actions">
|
||||||
|
<Button variant="secondary" on:click={() => handleEdit(index)}>Edit</Button>
|
||||||
|
<Button variant="danger" on:click={() => handleDelete(index)}>x</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.hint {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--color-text-secondary, #888);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target-header {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form {
|
||||||
|
background: var(--color-bg-secondary, #1a1a2e);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form h4 {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
color: var(--color-text-primary, #fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target-table {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--color-border, #333);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: var(--color-bg-secondary, #1a1a2e);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.editing {
|
||||||
|
background: var(--color-bg-hover, #252540);
|
||||||
|
}
|
||||||
|
|
||||||
|
.id-cell {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-text-secondary, #888);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-message {
|
||||||
|
color: var(--color-text-secondary, #888);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -41,6 +41,8 @@ interface RockConfig {
|
|||||||
angularVelocity: Vector3;
|
angularVelocity: Vector3;
|
||||||
scoreObservable: Observable<ScoreEvent>;
|
scoreObservable: Observable<ScoreEvent>;
|
||||||
useOrbitConstraint: boolean;
|
useOrbitConstraint: boolean;
|
||||||
|
targetPosition?: Vector3;
|
||||||
|
targetMode?: 'orbit' | 'moveToward';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RockFactory {
|
export class RockFactory {
|
||||||
@ -166,7 +168,9 @@ export class RockFactory {
|
|||||||
angularVelocity: Vector3,
|
angularVelocity: Vector3,
|
||||||
scoreObservable: Observable<ScoreEvent>,
|
scoreObservable: Observable<ScoreEvent>,
|
||||||
useOrbitConstraint: boolean = true,
|
useOrbitConstraint: boolean = true,
|
||||||
hidden: boolean = false
|
hidden: boolean = false,
|
||||||
|
targetPosition?: Vector3,
|
||||||
|
targetMode?: 'orbit' | 'moveToward'
|
||||||
): Rock {
|
): Rock {
|
||||||
if (!this._asteroidMesh) {
|
if (!this._asteroidMesh) {
|
||||||
throw new Error('[RockFactory] Asteroid mesh not loaded. Call initMesh() first.');
|
throw new Error('[RockFactory] Asteroid mesh not loaded. Call initMesh() first.');
|
||||||
@ -188,11 +192,13 @@ export class RockFactory {
|
|||||||
linearVelocity,
|
linearVelocity,
|
||||||
angularVelocity,
|
angularVelocity,
|
||||||
scoreObservable,
|
scoreObservable,
|
||||||
useOrbitConstraint
|
useOrbitConstraint,
|
||||||
|
targetPosition,
|
||||||
|
targetMode
|
||||||
};
|
};
|
||||||
this._createdRocks.set(rock.id, { mesh: rock, config });
|
this._createdRocks.set(rock.id, { mesh: rock, config });
|
||||||
|
|
||||||
log.debug(`[RockFactory] Created rock mesh ${rock.id} (hidden: ${hidden})`);
|
log.debug(`[RockFactory] Created rock mesh ${rock.id} (hidden: ${hidden}, target: ${targetMode || 'none'})`);
|
||||||
return new Rock(rock);
|
return new Rock(rock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,8 +221,11 @@ export class RockFactory {
|
|||||||
body.setMotionType(PhysicsMotionType.DYNAMIC);
|
body.setMotionType(PhysicsMotionType.DYNAMIC);
|
||||||
body.setCollisionCallbackEnabled(true);
|
body.setCollisionCallbackEnabled(true);
|
||||||
|
|
||||||
// Apply orbit constraint if enabled
|
// Handle target-based physics
|
||||||
if (config.useOrbitConstraint && this._orbitCenter) {
|
if (config.targetPosition && config.targetMode) {
|
||||||
|
this.applyTargetPhysics(body, config);
|
||||||
|
} else if (config.useOrbitConstraint && this._orbitCenter) {
|
||||||
|
// Legacy: orbit around origin if no specific target
|
||||||
const constraint = new DistanceConstraint(
|
const constraint = new DistanceConstraint(
|
||||||
Vector3.Distance(config.position, this._orbitCenter.body.transformNode.position),
|
Vector3.Distance(config.position, this._orbitCenter.body.transformNode.position),
|
||||||
DefaultScene.MainScene
|
DefaultScene.MainScene
|
||||||
@ -230,7 +239,10 @@ export class RockFactory {
|
|||||||
physicsPlugin.setActivationControl(body, PhysicsActivationControl.ALWAYS_ACTIVE);
|
physicsPlugin.setActivationControl(body, PhysicsActivationControl.ALWAYS_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply velocity (may be modified by applyTargetPhysics for moveToward mode)
|
||||||
|
if (!(config.targetPosition && config.targetMode === 'moveToward')) {
|
||||||
body.setLinearVelocity(config.linearVelocity);
|
body.setLinearVelocity(config.linearVelocity);
|
||||||
|
}
|
||||||
body.setAngularVelocity(config.angularVelocity);
|
body.setAngularVelocity(config.angularVelocity);
|
||||||
|
|
||||||
// Setup collision handler
|
// Setup collision handler
|
||||||
@ -239,6 +251,45 @@ export class RockFactory {
|
|||||||
log.debug(`[RockFactory] Physics initialized for ${rock.id}`);
|
log.debug(`[RockFactory] Physics initialized for ${rock.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply target-based physics (orbit or moveToward)
|
||||||
|
*/
|
||||||
|
private static applyTargetPhysics(body: PhysicsBody, config: RockConfig): void {
|
||||||
|
if (!config.targetPosition) return;
|
||||||
|
|
||||||
|
if (config.targetMode === 'orbit') {
|
||||||
|
// Create distance constraint to target position
|
||||||
|
// We need a static body at the target position for the constraint
|
||||||
|
const targetNode = new TransformNode(`target-${body.transformNode.id}`, DefaultScene.MainScene);
|
||||||
|
targetNode.position = config.targetPosition;
|
||||||
|
const targetBody = new PhysicsAggregate(
|
||||||
|
targetNode, PhysicsShapeType.SPHERE,
|
||||||
|
{ radius: 0.1, mass: 0 },
|
||||||
|
DefaultScene.MainScene
|
||||||
|
);
|
||||||
|
targetBody.body.setMotionType(PhysicsMotionType.STATIC);
|
||||||
|
|
||||||
|
const distance = Vector3.Distance(config.position, config.targetPosition);
|
||||||
|
const constraint = new DistanceConstraint(distance, DefaultScene.MainScene);
|
||||||
|
body.addConstraint(targetBody.body, constraint);
|
||||||
|
|
||||||
|
// Apply original velocity for orbiting
|
||||||
|
body.setLinearVelocity(config.linearVelocity);
|
||||||
|
} else if (config.targetMode === 'moveToward') {
|
||||||
|
// Calculate speed as sum of absolute velocity components
|
||||||
|
const speed = Math.abs(config.linearVelocity.x) +
|
||||||
|
Math.abs(config.linearVelocity.y) +
|
||||||
|
Math.abs(config.linearVelocity.z);
|
||||||
|
|
||||||
|
// Direction toward target
|
||||||
|
const direction = config.targetPosition.subtract(config.position).normalize();
|
||||||
|
|
||||||
|
// Final velocity = direction * speed
|
||||||
|
const velocity = direction.scale(speed);
|
||||||
|
body.setLinearVelocity(velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static setupCollisionHandler(body: PhysicsBody, scoreObservable: Observable<ScoreEvent>): void {
|
private static setupCollisionHandler(body: PhysicsBody, scoreObservable: Observable<ScoreEvent>): void {
|
||||||
body.getCollisionObservable().add((eventData) => {
|
body.getCollisionObservable().add((eventData) => {
|
||||||
if (eventData.type !== 'COLLISION_STARTED') return;
|
if (eventData.type !== 'COLLISION_STARTED') return;
|
||||||
|
|||||||
@ -111,16 +111,27 @@ interface PlanetConfig {
|
|||||||
rotation?: Vector3Array;
|
rotation?: Vector3Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target position for asteroids to orbit or move toward
|
||||||
|
*/
|
||||||
|
export interface TargetConfig {
|
||||||
|
id: string;
|
||||||
|
name: string; // Display name for editor
|
||||||
|
position: Vector3Array;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Individual asteroid configuration
|
* Individual asteroid configuration
|
||||||
*/
|
*/
|
||||||
interface AsteroidConfig {
|
export interface AsteroidConfig {
|
||||||
id: string;
|
id: string;
|
||||||
position: Vector3Array;
|
position: Vector3Array;
|
||||||
scale: number; // Uniform scale applied to all axes
|
scale: number; // Uniform scale applied to all axes
|
||||||
linearVelocity: Vector3Array;
|
linearVelocity: Vector3Array;
|
||||||
angularVelocity?: Vector3Array;
|
angularVelocity?: Vector3Array;
|
||||||
mass?: number;
|
mass?: number;
|
||||||
|
targetId?: string; // Reference to target from targets array
|
||||||
|
targetMode?: 'orbit' | 'moveToward'; // How asteroid interacts with target
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,6 +166,7 @@ export interface LevelConfig {
|
|||||||
startBase?: StartBaseConfig;
|
startBase?: StartBaseConfig;
|
||||||
sun: SunConfig;
|
sun: SunConfig;
|
||||||
starfield?: StarfieldConfig;
|
starfield?: StarfieldConfig;
|
||||||
|
targets?: TargetConfig[];
|
||||||
planets: PlanetConfig[];
|
planets: PlanetConfig[];
|
||||||
asteroids: AsteroidConfig[];
|
asteroids: AsteroidConfig[];
|
||||||
|
|
||||||
|
|||||||
@ -239,6 +239,15 @@ export class LevelDeserializer {
|
|||||||
const asteroidConfig = this.config.asteroids[i];
|
const asteroidConfig = this.config.asteroids[i];
|
||||||
const useOrbitConstraints = this.config.useOrbitConstraints !== false;
|
const useOrbitConstraints = this.config.useOrbitConstraints !== false;
|
||||||
|
|
||||||
|
// Resolve target position if specified
|
||||||
|
let targetPosition: Vector3 | undefined;
|
||||||
|
if (asteroidConfig.targetId && this.config.targets) {
|
||||||
|
const target = this.config.targets.find(t => t.id === asteroidConfig.targetId);
|
||||||
|
if (target) {
|
||||||
|
targetPosition = this.arrayToVector3(target.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create mesh only (no physics)
|
// Create mesh only (no physics)
|
||||||
RockFactory.createRockMesh(
|
RockFactory.createRockMesh(
|
||||||
i,
|
i,
|
||||||
@ -248,7 +257,9 @@ export class LevelDeserializer {
|
|||||||
this.arrayToVector3(asteroidConfig.angularVelocity),
|
this.arrayToVector3(asteroidConfig.angularVelocity),
|
||||||
scoreObservable,
|
scoreObservable,
|
||||||
useOrbitConstraints,
|
useOrbitConstraints,
|
||||||
hidden
|
hidden,
|
||||||
|
targetPosition,
|
||||||
|
asteroidConfig.targetMode
|
||||||
);
|
);
|
||||||
|
|
||||||
const mesh = this.scene.getMeshByName(asteroidConfig.id);
|
const mesh = this.scene.getMeshByName(asteroidConfig.id);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user