обновление от 2025-09-24 для ветки 24sepTest

This commit is contained in:
2025-09-24 08:29:53 +03:00
parent 261e8a8b63
commit 5d19b6339e
19 changed files with 2241 additions and 161 deletions

60
migrate-colliders.js Normal file
View File

@@ -0,0 +1,60 @@
// Скрипт для выполнения миграции таблицы colliders
// Файл: migrate-colliders.js
const fs = require('fs');
const path = require('path');
const { query } = require('./db');
async function runMigration() {
try {
console.log('🚀 Запуск миграции для создания таблицы colliders...');
// Читаем SQL файл миграции
const migrationPath = path.join(__dirname, 'migrations', 'create_colliders_table.sql');
const migrationSQL = fs.readFileSync(migrationPath, 'utf8');
// Выполняем миграцию
await query(migrationSQL);
console.log('✅ Миграция успешно выполнена!');
console.log('📊 Таблица colliders создана');
// Проверяем, что таблица создана
const result = await query(`
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'colliders'
ORDER BY ordinal_position
`);
console.log('📋 Структура таблицы colliders:');
result.rows.forEach(row => {
console.log(` - ${row.column_name}: ${row.data_type} (${row.is_nullable === 'YES' ? 'nullable' : 'not null'})`);
});
// Проверяем индексы
const indexes = await query(`
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'colliders'
`);
console.log('🔍 Индексы:');
indexes.rows.forEach(row => {
console.log(` - ${row.indexname}`);
});
} catch (error) {
console.error('❌ Ошибка при выполнении миграции:', error);
process.exit(1);
}
}
// Запускаем миграцию
runMigration().then(() => {
console.log('🎉 Миграция завершена успешно!');
process.exit(0);
}).catch(error => {
console.error('💥 Критическая ошибка:', error);
process.exit(1);
});

113
migrate-json-to-db.js Normal file
View File

@@ -0,0 +1,113 @@
// Скрипт для миграции коллайдеров из JSON в базу данных
// Файл: migrate-json-to-db.js
const fs = require('fs');
const path = require('path');
const { query } = require('./db');
async function migrateJsonToDb() {
try {
console.log('🚀 Запуск миграции коллайдеров из JSON в базу данных...');
// Читаем JSON файл
const jsonPath = path.join(__dirname, 'public', 'colliders_city_1.json');
if (!fs.existsSync(jsonPath)) {
console.log('❌ JSON файл не найден:', jsonPath);
return;
}
const jsonContent = fs.readFileSync(jsonPath, 'utf8');
const data = JSON.parse(jsonContent);
if (!data.colliders || !Array.isArray(data.colliders)) {
console.log('❌ Неверный формат JSON файла');
return;
}
console.log(`📊 Найдено ${data.colliders.length} коллайдеров в JSON файле`);
// Начинаем транзакцию
await query('BEGIN');
// Очищаем существующие коллайдеры для города 1
await query('DELETE FROM colliders WHERE city_id = $1', [1]);
console.log('🗑️ Очищены существующие коллайдеры для города 1');
// Вставляем коллайдеры из JSON
let insertedCount = 0;
for (const collider of data.colliders) {
try {
await query(`
INSERT INTO colliders (
city_id, type,
position_x, position_y, position_z,
rotation_x, rotation_y, rotation_z,
scale_x, scale_y, scale_z,
color_r, color_g, color_b,
opacity
) VALUES (
$1, $2,
$3, $4, $5,
$6, $7, $8,
$9, $10, $11,
$12, $13, $14,
$15
)
`, [
1, // city_id
collider.type || 'box',
collider.position?.x || 0,
collider.position?.y || 0,
collider.position?.z || 0,
collider.rotation?.x || 0,
collider.rotation?.y || 0,
collider.rotation?.z || 0,
collider.scale?.x || 1,
collider.scale?.y || 1,
collider.scale?.z || 1,
collider.color?.r || 1,
collider.color?.g || 0,
collider.color?.b || 0,
collider.opacity || 0.3
]);
insertedCount++;
console.log(`✅ Коллайдер ${insertedCount} мигрирован:`, {
type: collider.type,
position: collider.position,
color: collider.color
});
} catch (error) {
console.error(`❌ Ошибка при миграции коллайдера ${insertedCount + 1}:`, error);
throw error;
}
}
// Подтверждаем транзакцию
await query('COMMIT');
console.log(`🎉 Миграция завершена успешно!`);
console.log(`📊 Мигрировано ${insertedCount} коллайдеров из JSON в базу данных`);
// Проверяем результат
const result = await query('SELECT COUNT(*) as count FROM colliders WHERE city_id = $1', [1]);
console.log(`🔍 Проверка: в БД теперь ${result.rows[0].count} коллайдеров для города 1`);
} catch (error) {
// Откатываем транзакцию в случае ошибки
await query('ROLLBACK');
console.error('💥 Ошибка при миграции:', error);
process.exit(1);
}
}
// Запускаем миграцию
migrateJsonToDb().then(() => {
console.log('🚀 Миграция JSON -> БД завершена!');
process.exit(0);
}).catch(error => {
console.error('💥 Критическая ошибка:', error);
process.exit(1);
});

View File

@@ -0,0 +1,79 @@
-- Миграция для создания таблицы colliders
-- Файл: migrations/create_colliders_table.sql
-- Создание таблицы colliders
CREATE TABLE IF NOT EXISTS colliders (
id SERIAL PRIMARY KEY,
city_id INTEGER NOT NULL,
type VARCHAR(20) NOT NULL CHECK (type IN ('box', 'circle', 'capsule')),
-- Позиция
position_x DECIMAL(15, 6) NOT NULL DEFAULT 0,
position_y DECIMAL(15, 6) NOT NULL DEFAULT 0,
position_z DECIMAL(15, 6) NOT NULL DEFAULT 0,
-- Поворот (в радианах)
rotation_x DECIMAL(15, 6) NOT NULL DEFAULT 0,
rotation_y DECIMAL(15, 6) NOT NULL DEFAULT 0,
rotation_z DECIMAL(15, 6) NOT NULL DEFAULT 0,
-- Масштаб
scale_x DECIMAL(15, 6) NOT NULL DEFAULT 1,
scale_y DECIMAL(15, 6) NOT NULL DEFAULT 1,
scale_z DECIMAL(15, 6) NOT NULL DEFAULT 1,
-- Цвет (RGB 0-1)
color_r DECIMAL(3, 2) NOT NULL DEFAULT 1.0 CHECK (color_r >= 0 AND color_r <= 1),
color_g DECIMAL(3, 2) NOT NULL DEFAULT 0.0 CHECK (color_g >= 0 AND color_g <= 1),
color_b DECIMAL(3, 2) NOT NULL DEFAULT 0.0 CHECK (color_b >= 0 AND color_b <= 1),
-- Прозрачность (0-1)
opacity DECIMAL(3, 2) NOT NULL DEFAULT 0.3 CHECK (opacity >= 0 AND opacity <= 1),
-- Метаданные
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
-- Индексы для быстрого поиска
CONSTRAINT fk_colliders_city FOREIGN KEY (city_id) REFERENCES cities(id) ON DELETE CASCADE
);
-- Создание индексов для оптимизации запросов
CREATE INDEX IF NOT EXISTS idx_colliders_city_id ON colliders(city_id);
CREATE INDEX IF NOT EXISTS idx_colliders_type ON colliders(type);
CREATE INDEX IF NOT EXISTS idx_colliders_position ON colliders(position_x, position_y, position_z);
-- Создание функции для автоматического обновления updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ language 'plpgsql';
-- Создание триггера для автоматического обновления updated_at
CREATE TRIGGER update_colliders_updated_at
BEFORE UPDATE ON colliders
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- Комментарии к таблице и колонкам
COMMENT ON TABLE colliders IS 'Таблица коллайдеров для городов';
COMMENT ON COLUMN colliders.city_id IS 'ID города, к которому принадлежит коллайдер';
COMMENT ON COLUMN colliders.type IS 'Тип коллайдера: box, circle, capsule';
COMMENT ON COLUMN colliders.position_x IS 'X координата позиции';
COMMENT ON COLUMN colliders.position_y IS 'Y координата позиции';
COMMENT ON COLUMN colliders.position_z IS 'Z координата позиции';
COMMENT ON COLUMN colliders.rotation_x IS 'X компонент поворота (радианы)';
COMMENT ON COLUMN colliders.rotation_y IS 'Y компонент поворота (радианы)';
COMMENT ON COLUMN colliders.rotation_z IS 'Z компонент поворота (радианы)';
COMMENT ON COLUMN colliders.scale_x IS 'X компонент масштаба';
COMMENT ON COLUMN colliders.scale_y IS 'Y компонент масштаба';
COMMENT ON COLUMN colliders.scale_z IS 'Z компонент масштаба';
COMMENT ON COLUMN colliders.color_r IS 'Красный компонент цвета (0-1)';
COMMENT ON COLUMN colliders.color_g IS 'Зеленый компонент цвета (0-1)';
COMMENT ON COLUMN colliders.color_b IS 'Синий компонент цвета (0-1)';
COMMENT ON COLUMN colliders.opacity IS 'Прозрачность (0-1)';
COMMENT ON COLUMN colliders.created_at IS 'Время создания записи';
COMMENT ON COLUMN colliders.updated_at IS 'Время последнего обновления записи';

View File

@@ -1,96 +1,6 @@
{
"colliders": [
{
"type": "box",
"position": {
"x": -15.894407457818183,
"y": -98.46844400767294,
"z": -74.33953239204133
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 3.8820412781839853,
"y": 1.9275391013076184,
"z": 0.020423580261430187
},
"color": {
"r": 0.2,
"g": 1,
"b": 0.1
},
"opacity": 1
},
{
"type": "box",
"position": {
"x": -19.682621880668748,
"y": -98.44103033988638,
"z": -71.00143351764237
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 0.021,
"y": 1.9,
"z": 3.8
},
"color": {
"r": 0.2,
"g": 1,
"b": 0.1
},
"opacity": 1
},
{
"type": "box",
"position": {
"x": -16.32989632517387,
"y": -98.91158756288065,
"z": -66.6280016541835
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 1,
"y": 2,
"z": 1
},
"color": {
"r": 0.2,
"g": 1,
"b": 0.1
},
"opacity": 1
},
{
"type": "box",
"position": {
"x": 3.1476200534247045,
"y": -0.5512983469988768,
"z": -0.4395577458346196
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": {
"x": 0.5301919909798507,
"y": 2.40369930235243007,
"z": 1.2663514332721553
},
"color": {},
"opacity": 0.3
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
{"time":"2025-10-30T05:20:24.552Z","lastReal":1758297743627}
{"time":"2025-12-05T08:30:14.152Z","lastReal":1758687967327}

247
server.js
View File

@@ -1666,54 +1666,237 @@ app.get('/api/cities', authenticate, async (req, res) => {
}
});
// API endpoint для сохранения коллайдеров города
app.post('/api/colliders/city/:cityId', authenticate, async (req, res) => {
const cityId = req.params.cityId;
const collidersData = req.body;
// API endpoint для получения коллайдеров города из базы данных
app.get('/api/colliders/city/:cityId', authenticate, async (req, res) => {
const cityId = parseInt(req.params.cityId, 10);
try {
// Сохраняем коллайдеры в JSON файл
const fileName = `colliders_city_${cityId}.json`;
const filePath = pathLib.join(__dirname, 'public', fileName);
const { rows } = await db.query(`
SELECT
id,
type,
position_x as "position.x",
position_y as "position.y",
position_z as "position.z",
rotation_x as "rotation.x",
rotation_y as "rotation.y",
rotation_z as "rotation.z",
scale_x as "scale.x",
scale_y as "scale.y",
scale_z as "scale.z",
color_r as "color.r",
color_g as "color.g",
color_b as "color.b",
opacity,
created_at,
updated_at
FROM colliders
WHERE city_id = $1
ORDER BY created_at ASC
`, [cityId]);
// Создаем директорию если не существует
await fs.promises.mkdir(pathLib.dirname(filePath), { recursive: true });
// Преобразуем данные в формат, ожидаемый фронтендом
const colliders = rows.map(row => ({
id: row.id,
type: row.type,
position: {
x: parseFloat(row["position.x"]),
y: parseFloat(row["position.y"]),
z: parseFloat(row["position.z"])
},
rotation: {
x: parseFloat(row["rotation.x"]),
y: parseFloat(row["rotation.y"]),
z: parseFloat(row["rotation.z"])
},
scale: {
x: parseFloat(row["scale.x"]),
y: parseFloat(row["scale.y"]),
z: parseFloat(row["scale.z"])
},
color: {
r: parseFloat(row["color.r"]),
g: parseFloat(row["color.g"]),
b: parseFloat(row["color.b"])
},
opacity: parseFloat(row.opacity),
created_at: row.created_at,
updated_at: row.updated_at
}));
// Записываем данные в файл
await fs.promises.writeFile(filePath, JSON.stringify(collidersData, null, 2), 'utf8');
console.log(`Коллайдеры для города ${cityId} сохранены в ${fileName}`);
res.json({ success: true, message: 'Коллайдеры сохранены успешно' });
res.json({ colliders });
} catch (error) {
console.error('Ошибка сохранения коллайдеров:', error);
console.error('Ошибка получения коллайдеров из БД:', error);
res.status(500).json({ error: 'Ошибка получения коллайдеров' });
}
});
// API endpoint для сохранения коллайдеров города в базу данных
app.post('/api/colliders/city/:cityId', authenticate, async (req, res) => {
const cityId = parseInt(req.params.cityId, 10);
const { colliders } = req.body;
if (!Array.isArray(colliders)) {
return res.status(400).json({ error: 'Invalid colliders data' });
}
try {
// Начинаем транзакцию
await db.query('BEGIN');
// Разделяем коллайдеры на новые и существующие
const newColliders = colliders.filter(c => !c.id);
const existingColliders = colliders.filter(c => c.id);
console.log(`💾 Сохраняем: ${existingColliders.length} существующих, ${newColliders.length} новых коллайдеров`);
// Обновляем существующие коллайдеры
for (const collider of existingColliders) {
await db.query(`
UPDATE colliders SET
type = $2,
position_x = $3, position_y = $4, position_z = $5,
rotation_x = $6, rotation_y = $7, rotation_z = $8,
scale_x = $9, scale_y = $10, scale_z = $11,
color_r = $12, color_g = $13, color_b = $14,
opacity = $15,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1 AND city_id = $16
`, [
collider.id,
collider.type,
collider.position?.x || 0,
collider.position?.y || 0,
collider.position?.z || 0,
collider.rotation?.x || 0,
collider.rotation?.y || 0,
collider.rotation?.z || 0,
collider.scale?.x || 1,
collider.scale?.y || 1,
collider.scale?.z || 1,
collider.color?.r || 1,
collider.color?.g || 0,
collider.color?.b || 0,
collider.opacity || 0.3,
cityId
]);
}
// Вставляем новые коллайдеры
for (const collider of newColliders) {
await db.query(`
INSERT INTO colliders (
city_id, type,
position_x, position_y, position_z,
rotation_x, rotation_y, rotation_z,
scale_x, scale_y, scale_z,
color_r, color_g, color_b,
opacity
) VALUES (
$1, $2,
$3, $4, $5,
$6, $7, $8,
$9, $10, $11,
$12, $13, $14,
$15
)
`, [
cityId,
collider.type,
collider.position?.x || 0,
collider.position?.y || 0,
collider.position?.z || 0,
collider.rotation?.x || 0,
collider.rotation?.y || 0,
collider.rotation?.z || 0,
collider.scale?.x || 1,
collider.scale?.y || 1,
collider.scale?.z || 1,
collider.color?.r || 1,
collider.color?.g || 0,
collider.color?.b || 0,
collider.opacity || 0.3
]);
}
// Подтверждаем транзакцию
await db.query('COMMIT');
console.log(`✅ Коллайдеры для города ${cityId} сохранены в БД (${existingColliders.length} обновлено, ${newColliders.length} новых)`);
res.json({ success: true, message: 'Коллайдеры сохранены успешно', updated: existingColliders.length, created: newColliders.length });
} catch (error) {
// Откатываем транзакцию в случае ошибки
await db.query('ROLLBACK');
console.error('Ошибка сохранения коллайдеров в БД:', error);
res.status(500).json({ error: 'Ошибка сохранения коллайдеров' });
}
});
// API endpoint для получения коллайдеров города
app.get('/api/colliders/city/:cityId', authenticate, async (req, res) => {
const cityId = req.params.cityId;
// API endpoint для обновления отдельного коллайдера
app.put('/api/colliders/:colliderId', authenticate, async (req, res) => {
const colliderId = parseInt(req.params.colliderId, 10);
const collider = req.body;
try {
const fileName = `colliders_city_${cityId}.json`;
const filePath = pathLib.join(__dirname, 'public', fileName);
const { rowCount } = await db.query(`
UPDATE colliders SET
type = $2,
position_x = $3, position_y = $4, position_z = $5,
rotation_x = $6, rotation_y = $7, rotation_z = $8,
scale_x = $9, scale_y = $10, scale_z = $11,
color_r = $12, color_g = $13, color_b = $14,
opacity = $15,
updated_at = CURRENT_TIMESTAMP
WHERE id = $1
`, [
colliderId,
collider.type,
collider.position?.x || 0,
collider.position?.y || 0,
collider.position?.z || 0,
collider.rotation?.x || 0,
collider.rotation?.y || 0,
collider.rotation?.z || 0,
collider.scale?.x || 1,
collider.scale?.y || 1,
collider.scale?.z || 1,
collider.color?.r || 1,
collider.color?.g || 0,
collider.color?.b || 0,
collider.opacity || 0.3
]);
// Проверяем существование файла
try {
await fs.promises.access(filePath);
} catch (error) {
// Файл не существует, возвращаем пустой массив
return res.json({ colliders: [] });
if (rowCount === 0) {
return res.status(404).json({ error: 'Коллайдер не найден' });
}
// Читаем файл
const fileContent = await fs.promises.readFile(filePath, 'utf8');
const data = JSON.parse(fileContent);
console.log(`✅ Коллайдер ${colliderId} обновлен в БД`);
res.json({ success: true, message: 'Коллайдер обновлен успешно' });
res.json(data);
} catch (error) {
console.error('Ошибка чтения коллайдеров:', error);
res.status(500).json({ error: 'Ошибка чтения коллайдеров' });
console.error('Ошибка обновления коллайдера в БД:', error);
res.status(500).json({ error: 'Ошибка обновления коллайдера' });
}
});
// API endpoint для удаления отдельного коллайдера
app.delete('/api/colliders/:colliderId', authenticate, async (req, res) => {
const colliderId = parseInt(req.params.colliderId, 10);
try {
const { rowCount } = await db.query('DELETE FROM colliders WHERE id = $1', [colliderId]);
if (rowCount === 0) {
return res.status(404).json({ error: 'Коллайдер не найден' });
}
console.log(`✅ Коллайдер ${colliderId} удален из БД`);
res.json({ success: true, message: 'Коллайдер удален успешно' });
} catch (error) {
console.error('Ошибка удаления коллайдера из БД:', error);
res.status(500).json({ error: 'Ошибка удаления коллайдера' });
}
});

View File

@@ -616,6 +616,80 @@ function Game({ avatarUrl, gender }) {
});
};
// Функция для принудительной перезагрузки всех коллайдеров из базы данных
window.reloadAllColliders = async () => {
console.log('🔄 Принудительная перезагрузка всех коллайдеров...');
try {
// Перезагружаем коллизионные коллайдеры
await loadCollidersFromJSON(1);
console.log('✅ Коллизионные коллайдеры перезагружены');
// Перезагружаем визуальные коллайдеры
await loadCustomCollidersForCity(1);
console.log('✅ Визуальные коллайдеры перезагружены');
console.log('🎉 Все коллайдеры успешно перезагружены из базы данных');
} catch (error) {
console.error('❌ Ошибка при перезагрузке коллайдеров:', error);
}
};
// Функция для проверки состояния коллайдеров в базе данных
window.checkCollidersInDB = async () => {
console.log('🔍 Проверяем коллайдеры в базе данных...');
try {
const token = localStorage.getItem('token');
const response = await fetch('/api/colliders/city/1', {
headers: { Authorization: `Bearer ${token}` }
});
if (response.ok) {
const data = await response.json();
console.log('📊 Коллайдеры в БД:', data.colliders?.length || 0, 'штук');
console.log('🔍 Данные из БД:', data);
// Сравниваем с текущими коллайдерами в игре
console.log('📊 Коллизионные коллайдеры в игре:', jsonCollidersRef.current?.length || 0, 'штук');
console.log('📊 Визуальные коллайдеры в игре:', visualCollidersRef.current?.length || 0, 'штук');
return data;
} else {
console.error('❌ Ошибка загрузки коллайдеров из БД:', response.status);
}
} catch (error) {
console.error('❌ Ошибка при проверке коллайдеров в БД:', error);
}
};
// Функция для обновления прозрачности всех коллизионных объектов
window.updateColliderOpacity = (opacity) => {
console.log('👁️ Обновляем прозрачность всех коллизионных объектов:', opacity);
obstacles.forEach(obstacle => {
if (obstacle.mesh && obstacle.mesh.userData.isCustomCollider) {
obstacle.mesh.material.opacity = opacity;
if (opacity === 0) {
obstacle.mesh.material.visible = false;
obstacle.mesh.material.alphaTest = 0;
} else {
obstacle.mesh.material.visible = true;
obstacle.mesh.material.alphaTest = 0.1;
}
}
});
console.log('✅ Прозрачность обновлена для', obstacles.length, 'коллизионных объектов');
};
// Функция для включения/выключения отображения коллизионных объектов
window.toggleColliderVisibility = (visible) => {
console.log('👁️ Переключаем видимость коллизионных объектов:', visible);
obstacles.forEach(obstacle => {
if (obstacle.mesh && obstacle.mesh.userData.isCustomCollider) {
obstacle.mesh.visible = visible;
}
});
console.log('✅ Видимость коллизионных объектов:', visible ? 'включена' : 'выключена');
};
window.setColliderColor = (r, g, b) => {
console.log('🎨 Устанавливаем цвет коллайдеров:', { r, g, b });
visualCollidersRef.current.forEach(collider => {
@@ -1341,13 +1415,24 @@ function Game({ avatarUrl, gender }) {
}
};
// Функция для загрузки коллизионных данных из JSON
// Функция для загрузки коллизионных данных из базы данных с fallback на JSON
const loadCollidersFromJSON = async (cityId = 1) => {
console.log('🔍 loadCollidersFromJSON вызвана для города:', cityId);
try {
const url = `/colliders_city_${cityId}.json`;
console.log('🔍 Загружаем URL:', url);
const response = await fetch(url);
// Сначала пробуем загрузить из базы данных
const token = localStorage.getItem('token');
let response = await fetch(`/api/colliders/city/${cityId}`, {
headers: { Authorization: `Bearer ${token}` }
});
// Если новый API недоступен (500 ошибка), пробуем старый JSON API
if (!response.ok && response.status === 500) {
console.log('🔄 Новый API недоступен, пробуем старый JSON API...');
const url = `/colliders_city_${cityId}.json`;
console.log('🔍 Загружаем URL:', url);
response = await fetch(url);
}
console.log('🔍 Ответ сервера:', response.status, response.ok);
if (!response.ok) {
@@ -1357,10 +1442,26 @@ function Game({ avatarUrl, gender }) {
const data = await response.json();
console.log('🔍 Загруженные данные:', data);
console.log('Загружены коллизионные данные:', data.colliders.length, 'объектов');
// Преобразуем JSON данные в Box3 объекты
const colliderBoxes = data.colliders.map((colliderData, index) => {
// Обрабатываем данные в зависимости от источника
let collidersData;
if (data.colliders) {
// Данные из базы данных (уже в правильном формате)
collidersData = data.colliders;
console.log('📊 Загружены коллайдеры из базы данных:', collidersData.length, 'объектов');
console.log('🔍 Пример коллайдера из БД:', collidersData[0]);
} else if (Array.isArray(data)) {
// Данные из JSON файла (прямой массив)
collidersData = data;
console.log('📄 Загружены коллайдеры из JSON файла:', collidersData.length, 'объектов');
console.log('🔍 Пример коллайдера из JSON:', collidersData[0]);
} else {
console.warn('Неизвестный формат данных коллайдеров:', data);
return [];
}
// Преобразуем данные в Box3 объекты
const colliderBoxes = collidersData.map((colliderData, index) => {
const box = new THREE.Box3();
// Создаем центр бокса
@@ -3316,12 +3417,44 @@ function Game({ avatarUrl, gender }) {
async function loadCustomCollidersForCity(cityIdParam) {
try {
const cityIdNum = Number(cityIdParam) || 0;
const query = cityIdNum ? `?cityId=${encodeURIComponent(cityIdNum)}` : '';
const res = await fetch(`/api/colliders${query}`, { cache: 'no-store', headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) return;
const cityIdNum = Number(cityIdParam) || 1;
console.log('🔍 loadCustomCollidersForCity для города:', cityIdNum);
// Сначала пробуем новый API с базой данных
let res = await fetch(`/api/colliders/city/${cityIdNum}`, {
cache: 'no-store',
headers: { Authorization: `Bearer ${token}` }
});
// Если новый API недоступен (500 ошибка), пробуем старый JSON API
if (!res.ok && res.status === 500) {
console.log('🔄 Новый API недоступен в loadCustomCollidersForCity, пробуем старый JSON API...');
const query = cityIdNum ? `?cityId=${encodeURIComponent(cityIdNum)}` : '';
res = await fetch(`/api/colliders${query}`, { cache: 'no-store', headers: { Authorization: `Bearer ${token}` } });
}
if (!res.ok) {
console.warn('Не удалось загрузить кастомные коллайдеры для города:', cityIdNum);
return;
}
const data = await res.json();
const list = Array.isArray(data?.colliders) ? data.colliders : [];
console.log('🔍 Загруженные данные кастомных коллайдеров:', data);
// Обрабатываем данные в зависимости от источника
let list;
if (data.colliders) {
// Данные из базы данных
list = data.colliders;
console.log('📊 Загружены кастомные коллайдеры из базы данных:', list.length, 'объектов');
} else if (Array.isArray(data)) {
// Данные из JSON файла
list = data;
console.log('📄 Загружены кастомные коллайдеры из JSON файла:', list.length, 'объектов');
} else {
console.warn('Неизвестный формат данных кастомных коллайдеров:', data);
return;
}
// Удаляем старые кастомные коллайдеры
obstacles = obstacles.filter(o => {
const keep = !o?.mesh?.userData?.isCustomCollider;
@@ -3336,7 +3469,25 @@ function Game({ avatarUrl, gender }) {
if (c.type === 'circle') geometry = new THREE.CylinderGeometry(1.5, 1.5, 2, 24);
else if (c.type === 'capsule') geometry = new THREE.CapsuleGeometry(1, 2, 4, 12);
else geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.001, depthWrite: false });
// Используем прозрачность из базы данных
const opacity = c.opacity !== undefined ? c.opacity : 0.001;
const material = new THREE.MeshBasicMaterial({
color: 0x000000,
transparent: true,
opacity: opacity,
depthWrite: false
});
// Если прозрачность 0, делаем материал невидимым
if (opacity === 0) {
material.visible = false;
material.alphaTest = 0;
} else {
material.visible = true;
material.alphaTest = 0.1;
}
const mesh = new THREE.Mesh(geometry, material);
const p = c.position || {}; const r = c.rotation || {}; const s = c.scale || {};
mesh.position.set(p.x || 0, p.y || 0, p.z || 0);
@@ -3348,7 +3499,7 @@ function Game({ avatarUrl, gender }) {
});
buildPathfindingGrid?.();
} catch (e) {
console.warn('Не удалось загрузить colliders.json', e);
console.warn('Не удалось загрузить кастомные коллайдеры', e);
}
}

View File

@@ -52,13 +52,32 @@ export default function EnhancedCollisionEditor() {
// Состояние для режима TransformControls
const [transformMode, setTransformMode] = useState('translate'); // 'translate', 'rotate', 'scale'
// Состояние для автоматического сохранения
const [autoSave, setAutoSave] = useState(true);
const [lastSaved, setLastSaved] = useState(null);
const autoSaveTimeoutRef = useRef(null);
const [showWireframes, setShowWireframes] = useState(true); // Новое состояние для рамок
const colliderMaterial = useMemo(() => new THREE.MeshBasicMaterial({
color: new THREE.Color(selectedColor.r, selectedColor.g, selectedColor.b),
transparent: true,
opacity: selectedOpacity,
depthWrite: false
}), [selectedColor, selectedOpacity]);
const colliderMaterial = useMemo(() => {
const material = new THREE.MeshBasicMaterial({
color: new THREE.Color(selectedColor.r, selectedColor.g, selectedColor.b),
transparent: true,
opacity: selectedOpacity,
depthWrite: false
});
// Если прозрачность 0, делаем материал невидимым
if (selectedOpacity === 0) {
material.visible = false;
material.alphaTest = 0;
} else {
material.visible = true;
material.alphaTest = 0.1;
}
return material;
}, [selectedColor, selectedOpacity]);
const colliderEdgeMaterial = useMemo(() => new THREE.LineBasicMaterial({
color: new THREE.Color(selectedColor.r, selectedColor.g, selectedColor.b)
@@ -107,6 +126,17 @@ export default function EnhancedCollisionEditor() {
transform.addEventListener('objectChange', () => {
if (selected) {
updateColliderData(selected);
// Обновляем параметры в UI при изменении через TransformControls
setColliderPosition({ x: selected.position.x, y: selected.position.y, z: selected.position.z });
setColliderRotation({ x: selected.rotation.x, y: selected.rotation.y, z: selected.rotation.z });
setColliderScale({ x: selected.scale.x, y: selected.scale.y, z: selected.scale.z });
console.log('🔄 Параметры обновлены через TransformControls:', {
position: selected.position,
rotation: selected.rotation,
scale: selected.scale
});
// Запускаем автоматическое сохранение
triggerAutoSave();
}
});
scene.add(transform);
@@ -356,12 +386,21 @@ export default function EnhancedCollisionEditor() {
setIsLoading(true);
try {
const token = localStorage.getItem('token');
const response = await fetch(`/api/colliders/city/${cityId}`, {
// Сначала пробуем новый API с базой данных
let response = await fetch(`/api/colliders/city/${cityId}`, {
headers: { Authorization: `Bearer ${token}` }
});
// Если новый API недоступен (500 ошибка), пробуем старый JSON API
if (!response.ok && response.status === 500) {
console.log('🔄 Новый API недоступен, пробуем старый JSON API...');
response = await fetch(`/api/colliders?cityId=${cityId}`, {
headers: { Authorization: `Bearer ${token}` }
});
}
if (!response.ok) {
console.log('JSON файл не найден, создаем новый');
console.log('📄 JSON файл не найден, создаем новый');
collidersRef.current = [];
return;
}
@@ -380,6 +419,7 @@ export default function EnhancedCollisionEditor() {
const mesh = new THREE.Mesh(geom, colliderMaterial.clone());
const edges = new THREE.EdgesGeometry(mesh.geometry);
const line = new THREE.LineSegments(edges, colliderEdgeMaterial.clone());
line.visible = showWireframes; // Учитываем настройку видимости рамок
mesh.add(line);
mesh.position.set(c.position?.x || 0, c.position?.y || 0, c.position?.z || 0);
@@ -394,12 +434,23 @@ export default function EnhancedCollisionEditor() {
}
if (c.opacity !== undefined) {
mesh.material.opacity = c.opacity;
// Если прозрачность 0, делаем материал невидимым
if (c.opacity === 0) {
mesh.material.visible = false;
mesh.material.transparent = true;
mesh.material.alphaTest = 0;
} else {
mesh.material.visible = true;
mesh.material.transparent = true;
mesh.material.alphaTest = 0.1;
}
}
mesh.userData = {
type: c.type || 'box',
color: c.color || { r: 1, g: 0, b: 0 },
opacity: c.opacity || 0.3
opacity: c.opacity || 0.3,
id: c.id // Добавляем ID из базы данных
};
sceneRef.current.add(mesh);
@@ -438,7 +489,8 @@ export default function EnhancedCollisionEditor() {
z: mesh.scale.z
},
color: mesh.userData.color || { r: 1, g: 0, b: 0 },
opacity: mesh.userData.opacity || 0.3
opacity: mesh.userData.opacity || 0.3,
id: mesh.userData.id // Добавляем ID для существующих коллайдеров
};
});
@@ -446,7 +498,9 @@ export default function EnhancedCollisionEditor() {
// Отправляем данные на сервер для сохранения
const token = localStorage.getItem('token');
const response = await fetch(`/api/colliders/city/${cityId}`, {
// Сначала пробуем новый API с базой данных
let response = await fetch(`/api/colliders/city/${cityId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -454,6 +508,19 @@ export default function EnhancedCollisionEditor() {
},
body: JSON.stringify(jsonData)
});
// Если новый API недоступен (500 ошибка), пробуем старый JSON API
if (!response.ok && response.status === 500) {
console.log('🔄 Новый API недоступен, пробуем старый JSON API...');
response = await fetch('/api/colliders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ ...jsonData, cityId: parseInt(cityId) })
});
}
if (response.ok) {
const result = await response.json();
@@ -482,6 +549,7 @@ export default function EnhancedCollisionEditor() {
const mesh = new THREE.Mesh(geom, colliderMaterial.clone());
const edges = new THREE.EdgesGeometry(mesh.geometry);
const line = new THREE.LineSegments(edges, colliderEdgeMaterial.clone());
line.visible = showWireframes; // Учитываем настройку видимости рамок
mesh.add(line);
// Вычисляем позицию перед камерой
@@ -503,7 +571,8 @@ export default function EnhancedCollisionEditor() {
mesh.userData = {
type: shapeType,
color: { ...selectedColor },
opacity: selectedOpacity
opacity: selectedOpacity,
id: null // Новый коллайдер пока не имеет ID
};
sceneRef.current.add(mesh);
@@ -513,6 +582,8 @@ export default function EnhancedCollisionEditor() {
console.log(`✅ Создан коллайдер типа "${shapeType}" в позиции:`, position);
console.log(`📊 Всего коллайдеров: ${collidersRef.current.length}`);
// Запускаем автоматическое сохранение
triggerAutoSave();
};
// Функция для обновления предварительного просмотра позиции коллайдера
@@ -620,6 +691,7 @@ export default function EnhancedCollisionEditor() {
const mesh = new THREE.Mesh(geom, newMaterial);
const edges = new THREE.EdgesGeometry(mesh.geometry);
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x000000 }));
line.visible = showWireframes; // Учитываем настройку видимости рамок
mesh.add(line);
// Копируем параметры с небольшим смещением
@@ -637,7 +709,8 @@ export default function EnhancedCollisionEditor() {
mesh.userData = {
type: selected.userData.type,
color: { ...selected.userData.color },
opacity: selected.userData.opacity
opacity: selected.userData.opacity,
id: null // Дублированный коллайдер пока не имеет ID
};
sceneRef.current.add(mesh);
@@ -652,6 +725,8 @@ export default function EnhancedCollisionEditor() {
rotation: { x: mesh.rotation.x, y: mesh.rotation.y, z: mesh.rotation.z },
scale: { x: mesh.scale.x, y: mesh.scale.y, z: mesh.scale.z }
});
// Запускаем автоматическое сохранение
triggerAutoSave();
};
// Функция для телепортации камеры к интерьеру
@@ -679,6 +754,31 @@ export default function EnhancedCollisionEditor() {
console.log(`Режим TransformControls изменен на: ${mode}`);
};
// Функция для принудительной перезагрузки коллайдеров из базы данных
const reloadColliders = async () => {
console.log('🔄 Принудительная перезагрузка коллайдеров из БД...');
await loadCollidersFromJSON();
console.log('✅ Коллайдеры перезагружены');
};
// Функция для переключения видимости рамок
const toggleWireframes = () => {
const newShowWireframes = !showWireframes;
setShowWireframes(newShowWireframes);
// Обновляем видимость рамок у всех коллайдеров
collidersRef.current.forEach(c => {
const mesh = c.mesh;
mesh.children.forEach(child => {
if (child.type === 'LineSegments') {
child.visible = newShowWireframes;
}
});
});
console.log(`🔲 Рамки ${newShowWireframes ? 'включены' : 'отключены'}`);
};
// Функция для отладки коллайдеров
const debugColliders = () => {
console.log('🔍 Отладка коллайдеров:');
@@ -695,6 +795,27 @@ export default function EnhancedCollisionEditor() {
});
};
// Функция автоматического сохранения
const triggerAutoSave = () => {
if (!autoSave || !cityId) return;
// Очищаем предыдущий таймер
if (autoSaveTimeoutRef.current) {
clearTimeout(autoSaveTimeoutRef.current);
}
// Устанавливаем новый таймер на 2 секунды
autoSaveTimeoutRef.current = setTimeout(async () => {
try {
await saveCollidersToJSON();
setLastSaved(new Date());
console.log('💾 Автоматическое сохранение выполнено');
} catch (error) {
console.error('❌ Ошибка автоматического сохранения:', error);
}
}, 2000);
};
// Обновление цвета выбранного коллайдера
const updateSelectedColliderColor = () => {
if (selected) {
@@ -704,12 +825,24 @@ export default function EnhancedCollisionEditor() {
selected.userData.color = { ...selectedColor };
selected.userData.opacity = selectedOpacity;
selected.material.opacity = selectedOpacity;
// Если прозрачность 0, делаем материал невидимым
if (selectedOpacity === 0) {
selected.material.visible = false;
selected.material.transparent = true;
selected.material.alphaTest = 0;
} else {
selected.material.visible = true;
selected.material.transparent = true;
selected.material.alphaTest = 0.1;
}
updateColliderData(selected);
}
};
// Удаление выбранного коллайдера
const deleteSelected = () => {
const deleteSelected = async () => {
if (!selected) {
console.log('❌ Нет выбранного коллайдера для удаления');
return;
@@ -718,6 +851,27 @@ export default function EnhancedCollisionEditor() {
console.log('🗑️ Удаляем коллайдер:', selected);
console.log('📊 Всего коллайдеров до удаления:', collidersRef.current.length);
// Если у коллайдера есть ID, удаляем его из базы данных
if (selected.userData.id) {
try {
const token = localStorage.getItem('token');
const response = await fetch(`/api/colliders/${selected.userData.id}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` }
});
if (response.ok) {
console.log(`✅ Коллайдер ${selected.userData.id} удален из базы данных`);
} else {
console.error(`❌ Ошибка удаления коллайдера ${selected.userData.id} из БД:`, await response.text());
}
} catch (error) {
console.error('❌ Ошибка при удалении коллайдера из БД:', error);
}
} else {
console.log('⚠️ У коллайдера нет ID, удаляем только из фронтенда');
}
// Удаляем из сцены
sceneRef.current.remove(selected);
@@ -733,6 +887,8 @@ export default function EnhancedCollisionEditor() {
setSelected(null);
console.log('✅ Коллайдер успешно удален');
// Запускаем автоматическое сохранение для синхронизации
triggerAutoSave();
};
// Обработка клика по объекту
@@ -744,24 +900,45 @@ export default function EnhancedCollisionEditor() {
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, cameraRef.current);
const intersects = raycaster.intersectObjects(collidersRef.current.map(c => c.mesh));
// Получаем все объекты коллайдеров (включая рамки)
const allColliderObjects = [];
collidersRef.current.forEach(c => {
allColliderObjects.push(c.mesh); // Основной меш
allColliderObjects.push(...c.mesh.children); // Рамки (дочерние объекты)
});
const intersects = raycaster.intersectObjects(allColliderObjects);
if (intersects.length > 0) {
const clickedMesh = intersects[0].object;
setSelected(clickedMesh);
transformRef.current.attach(clickedMesh);
let clickedObject = intersects[0].object;
console.log('🎯 Выбран коллайдер:', clickedMesh);
// Если кликнули по рамке, находим родительский меш
if (clickedObject.type === 'LineSegments') {
clickedObject = clickedObject.parent;
console.log('🎯 Кликнули по рамке, выбираем родительский меш:', clickedObject);
}
// Проверяем, что это действительно коллайдер из нашего списка
const collider = collidersRef.current.find(c => c.mesh === clickedObject);
if (!collider) {
console.log('❌ Кликнули по объекту, который не является коллайдером');
return;
}
setSelected(clickedObject);
transformRef.current.attach(clickedObject);
console.log('🎯 Выбран коллайдер:', clickedObject);
console.log('📊 Всего коллайдеров в массиве:', collidersRef.current.length);
// Обновляем цветовую палитру
setSelectedColor(clickedMesh.userData.color || { r: 1, g: 0, b: 0 });
setSelectedOpacity(clickedMesh.userData.opacity || 0.3);
setSelectedColor(clickedObject.userData.color || { r: 1, g: 0, b: 0 });
setSelectedOpacity(clickedObject.userData.opacity || 0.3);
// Обновляем параметры коллайдера
setColliderPosition({ x: clickedMesh.position.x, y: clickedMesh.position.y, z: clickedMesh.position.z });
setColliderRotation({ x: clickedMesh.rotation.x, y: clickedMesh.rotation.y, z: clickedMesh.rotation.z });
setColliderScale({ x: clickedMesh.scale.x, y: clickedMesh.scale.y, z: clickedMesh.scale.z });
setColliderPosition({ x: clickedObject.position.x, y: clickedObject.position.y, z: clickedObject.position.z });
setColliderRotation({ x: clickedObject.rotation.x, y: clickedObject.rotation.y, z: clickedObject.rotation.z });
setColliderScale({ x: clickedObject.scale.x, y: clickedObject.scale.y, z: clickedObject.scale.z });
} else {
setSelected(null);
transformRef.current.detach();
@@ -1080,6 +1257,25 @@ export default function EnhancedCollisionEditor() {
Масштаб
</button>
</div>
{/* Переключение рамок */}
<div style={{ marginBottom: '8px' }}>
<button
onClick={toggleWireframes}
style={{
width: '100%',
padding: '8px',
backgroundColor: showWireframes ? '#27ae60' : '#e74c3c',
color: 'white',
border: 'none',
borderRadius: '3px',
cursor: 'pointer',
fontSize: '11px'
}}
>
{showWireframes ? '🔲 Скрыть рамки' : '🔲 Показать рамки'}
</button>
</div>
</div>
<div style={{ marginBottom: '10px' }}>
@@ -1357,6 +1553,82 @@ export default function EnhancedCollisionEditor() {
)}
</div>
{/* Управление сохранением */}
<div style={{ marginBottom: '20px' }}>
<h3>Управление сохранением</h3>
<div style={{ marginBottom: '10px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<input
type="checkbox"
checked={autoSave}
onChange={(e) => setAutoSave(e.target.checked)}
style={{ transform: 'scale(1.2)' }}
/>
<span style={{ fontSize: '14px' }}>Автоматическое сохранение</span>
</label>
</div>
{lastSaved && (
<div style={{ fontSize: '12px', color: '#27ae60', marginBottom: '10px' }}>
💾 Последнее сохранение: {lastSaved.toLocaleTimeString()}
</div>
)}
<div style={{ display: 'flex', gap: '10px' }}>
<button
onClick={saveCollidersToJSON}
disabled={isSaving}
style={{
flex: 1,
padding: '8px',
backgroundColor: isSaving ? '#95a5a6' : '#27ae60',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: isSaving ? 'not-allowed' : 'pointer',
fontSize: '12px'
}}
>
{isSaving ? 'Сохранение...' : '💾 Сохранить сейчас'}
</button>
<button
onClick={loadCollidersFromJSON}
disabled={isLoading}
style={{
flex: 1,
padding: '8px',
backgroundColor: isLoading ? '#95a5a6' : '#3498db',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: isLoading ? 'not-allowed' : 'pointer',
fontSize: '12px'
}}
>
{isLoading ? 'Загрузка...' : '🔄 Перезагрузить'}
</button>
<button
onClick={reloadColliders}
disabled={isLoading}
style={{
flex: 1,
padding: '8px',
backgroundColor: isLoading ? '#95a5a6' : '#e67e22',
color: 'white',
border: 'none',
borderRadius: '5px',
cursor: isLoading ? 'not-allowed' : 'pointer',
fontSize: '12px'
}}
>
{isLoading ? 'Загрузка...' : '🔄 Синхронизировать'}
</button>
</div>
</div>
{/* Информация */}
<div style={{ marginTop: '20px', fontSize: '12px', color: '#bdc3c7' }}>
<p>Коллайдеров: {collidersRef.current.length}</p>

View File

@@ -0,0 +1,174 @@
// Исправление проблемы с удалением коллайдеров
// Файл: test-collider-deletion-fix.js
console.log('🔧 Исправление проблемы с удалением коллайдеров');
console.log('');
console.log('❓ Проблема:');
console.log('Не удаётся удалить старый коллайдер');
console.log('Удаляю, сохраняю, но после перезагрузки он опять появляется');
console.log('');
console.log('🔍 Причина проблемы:');
console.log('');
console.log('1. 🗑️ Неполное удаление:');
console.log(' - Функция deleteSelected удаляла коллайдер только из фронтенда');
console.log(' - Коллайдер оставался в базе данных');
console.log(' - При перезагрузке коллайдер загружался обратно из БД');
console.log('');
console.log('2. 🔄 Проблема с автоматическим сохранением:');
console.log(' - Автоматическое сохранение полагалось на перезапись всех коллайдеров');
console.log(' - Если сохранение не сработало или сработало с ошибкой, коллайдер оставался');
console.log(' - Отсутствие прямого удаления из БД');
console.log('');
console.log('3. 🆔 Отсутствие ID у новых коллайдеров:');
console.log(' - Новые коллайдеры создавались без ID');
console.log(' - Их нельзя было удалить через API');
console.log(' - Только автоматическое сохранение могло их удалить');
console.log('');
console.log('✅ Исправления:');
console.log('');
console.log('1. 🗑️ Прямое удаление из базы данных:');
console.log(' - Функция deleteSelected теперь асинхронная');
console.log(' - Если у коллайдера есть ID, он удаляется из БД через API');
console.log(' - DELETE /api/colliders/:colliderId');
console.log('');
console.log('2. 🔄 Улучшенная обработка ошибок:');
console.log(' - Логирование успешного удаления из БД');
console.log(' - Обработка ошибок при удалении');
console.log(' - Fallback для коллайдеров без ID');
console.log('');
console.log('3. 🔄 Функция синхронизации:');
console.log(' - Новая функция reloadColliders() для принудительной перезагрузки');
console.log(' - Кнопка "🔄 Синхронизировать" в UI');
console.log(' - Возможность проверить состояние БД');
console.log('');
console.log('4. 📊 Улучшенное логирование:');
console.log(' - Подробные логи процесса удаления');
console.log(' - Информация об ID коллайдеров');
console.log(' - Отслеживание количества коллайдеров');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('🗑️ Логика удаления:');
console.log('- Проверка наличия ID: if (selected.userData.id)');
console.log('- DELETE запрос: /api/colliders/${selected.userData.id}');
console.log('- Удаление из сцены: sceneRef.current.remove(selected)');
console.log('- Удаление из массива: collidersRef.current.filter()');
console.log('- Автоматическое сохранение для синхронизации');
console.log('');
console.log('🔄 API endpoints:');
console.log('- DELETE /api/colliders/:colliderId - удаление конкретного коллайдера');
console.log('- POST /api/colliders/city/:cityId - перезапись всех коллайдеров');
console.log('- GET /api/colliders/city/:cityId - загрузка коллайдеров');
console.log('');
console.log('🆔 ID система:');
console.log('- Старые коллайдеры: имеют ID из базы данных');
console.log('- Новые коллайдеры: id: null до первого сохранения');
console.log('- Дублированные: id: null до первого сохранения');
console.log('- После сохранения: получают ID из БД');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 🗑️ Удаление старых коллайдеров:');
console.log(' - Выберите коллайдер, который был создан ранее');
console.log(' - Нажмите "Удалить коллайдер"');
console.log(' - Проверьте консоль: "✅ Коллайдер X удален из базы данных"');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что коллайдер не появился');
console.log('');
console.log('2. 🆕 Удаление новых коллайдеров:');
console.log(' - Создайте новый коллайдер');
console.log(' - Сохраните его (чтобы получить ID)');
console.log(' - Удалите коллайдер');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что коллайдер удален');
console.log('');
console.log('3. 🔄 Синхронизация:');
console.log(' - Удалите несколько коллайдеров');
console.log(' - Нажмите "🔄 Синхронизировать"');
console.log(' - Проверьте консоль: "🔄 Принудительная перезагрузка коллайдеров из БД"');
console.log(' - Убедитесь, что удаленные коллайдеры не загрузились');
console.log('');
console.log('4. 📊 Отладочная информация:');
console.log(' - Проверьте консоль на наличие ошибок');
console.log(' - Должны быть логи о процессе удаления');
console.log(' - Проверьте количество коллайдеров до и после удаления');
console.log('');
console.log('5. 🎯 Тест с несколькими коллайдерами:');
console.log(' - Создайте несколько коллайдеров');
console.log(' - Удалите некоторые из них');
console.log(' - Сохраните изменения');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что только нужные коллайдеры остались');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Удаление старых коллайдеров:');
console.log('- [ ] Коллайдеры с ID удаляются из базы данных');
console.log('- [ ] После перезагрузки коллайдеры не появляются');
console.log('- [ ] Логирование успешного удаления');
console.log('- [ ] Отсутствие ошибок в консоли');
console.log('');
console.log('✅ Удаление новых коллайдеров:');
console.log('- [ ] Новые коллайдеры удаляются из фронтенда');
console.log('- [ ] Автоматическое сохранение синхронизирует изменения');
console.log('- [ ] После перезагрузки коллайдеры не появляются');
console.log('- [ ] Корректная работа с коллайдерами без ID');
console.log('');
console.log('✅ Синхронизация:');
console.log('- [ ] Кнопка "Синхронизировать" работает');
console.log('- [ ] Принудительная перезагрузка из БД');
console.log('- [ ] Синхронизация фронтенда с базой данных');
console.log('- [ ] Отсутствие рассинхронизации');
console.log('');
console.log('✅ Обработка ошибок:');
console.log('- [ ] Логирование ошибок при удалении');
console.log('- [ ] Graceful fallback для коллайдеров без ID');
console.log('- [ ] Отсутствие критических ошибок');
console.log('- [ ] Стабильная работа системы');
console.log('');
console.log('🔍 Отладочная информация:');
console.log('');
console.log('📊 В консоли должно появляться:');
console.log('- "🗑️ Удаляем коллайдер: [объект]"');
console.log('- "✅ Коллайдер X удален из базы данных"');
console.log('- "📊 Коллайдеров до: X, после: Y"');
console.log('- "✅ Коллайдер успешно удален"');
console.log('- "💾 Автоматическое сохранение выполнено"');
console.log('');
console.log('🎯 Проверка в базе данных:');
console.log('- SELECT * FROM colliders WHERE city_id = 1;');
console.log('- Удаленные коллайдеры не должны присутствовать');
console.log('- Проверить updated_at для оставшихся коллайдеров');
console.log('');
console.log('🚀 Исправления готовы к тестированию!');
console.log('Откройте: http://localhost:4000/enhanced-collision-editor');
console.log('Попробуйте удалить коллайдер и проверить, что он не появляется после перезагрузки');

View File

@@ -0,0 +1,160 @@
// Тест исправления сохранения перемещенных коллайдеров
// Файл: test-collider-position-saving.js
console.log('🔧 Исправление сохранения перемещенных коллайдеров');
console.log('');
console.log('❓ Проблема:');
console.log('Перемещаю старый коллайдер, нажимаю "сохранить", но при новом входе он стоит на том же месте');
console.log('');
console.log('🔍 Причина проблемы:');
console.log('');
console.log('1. 🆔 Отсутствие ID у коллайдеров:');
console.log(' - Старые коллайдеры загружались из JSON без ID');
console.log(' - Новые коллайдеры создавались без ID');
console.log(' - Сервер не мог различить существующие и новые коллайдеры');
console.log('');
console.log('2. 🔄 Неправильная логика сохранения:');
console.log(' - Сервер удалял ВСЕ коллайдеры и создавал заново');
console.log(' - Потеря связи между фронтендом и базой данных');
console.log(' - Новые позиции не сохранялись');
console.log('');
console.log('✅ Исправления:');
console.log('');
console.log('1. 🆔 Добавление ID к коллайдерам:');
console.log(' - Старые коллайдеры получают ID из базы данных при загрузке');
console.log(' - Новые коллайдеры помечаются как id: null');
console.log(' - Дублированные коллайдеры помечаются как id: null');
console.log('');
console.log('2. 🔄 Улучшенная логика сохранения:');
console.log(' - Разделение коллайдеров на существующие (с ID) и новые (без ID)');
console.log(' - UPDATE для существующих коллайдеров');
console.log(' - INSERT для новых коллайдеров');
console.log(' - Сохранение связи между фронтендом и БД');
console.log('');
console.log('3. 🗄️ Обновление серверной логики:');
console.log(' - Фильтрация коллайдеров по наличию ID');
console.log(' - UPDATE запросы для существующих коллайдеров');
console.log(' - INSERT запросы для новых коллайдеров');
console.log(' - Логирование количества обновленных и созданных коллайдеров');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('🆔 ID система:');
console.log('- Старые коллайдеры: mesh.userData.id = c.id (из БД)');
console.log('- Новые коллайдеры: mesh.userData.id = null');
console.log('- Дублированные: mesh.userData.id = null');
console.log('- При сохранении: id: mesh.userData.id');
console.log('');
console.log('🔄 Логика сохранения:');
console.log('- existingColliders = colliders.filter(c => c.id)');
console.log('- newColliders = colliders.filter(c => !c.id)');
console.log('- UPDATE для существующих с WHERE id = $1');
console.log('- INSERT для новых без ID');
console.log('');
console.log('🗄️ Серверные изменения:');
console.log('- Разделение коллайдеров на две группы');
console.log('- UPDATE запросы для существующих');
console.log('- INSERT запросы для новых');
console.log('- Транзакционная безопасность');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 🔄 Перемещение существующих коллайдеров:');
console.log(' - Загрузите редактор коллизий');
console.log(' - Выберите один из старых коллайдеров');
console.log(' - Переместите его в новое место');
console.log(' - Нажмите "Сохранить сейчас"');
console.log(' - Проверьте консоль: должно появиться "💾 Сохраняем: X существующих, Y новых"');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что коллайдер остался в новом месте');
console.log('');
console.log('2. 🆕 Создание новых коллайдеров:');
console.log(' - Создайте новый коллайдер');
console.log(' - Переместите его в нужное место');
console.log(' - Нажмите "Сохранить сейчас"');
console.log(' - Проверьте консоль: должно появиться сообщение о создании');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что новый коллайдер сохранился');
console.log('');
console.log('3. 🔄 Дублирование коллайдеров:');
console.log(' - Выберите существующий коллайдер');
console.log(' - Нажмите "Дублировать коллайдер"');
console.log(' - Переместите дублированный коллайдер');
console.log(' - Нажмите "Сохранить сейчас"');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что оба коллайдера сохранились');
console.log('');
console.log('4. 🎯 Автоматическое сохранение:');
console.log(' - Переместите коллайдер');
console.log(' - Подождите 2 секунды');
console.log(' - Проверьте консоль: "💾 Автоматическое сохранение выполнено"');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что изменения сохранились');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Перемещение существующих:');
console.log('- [ ] Коллайдеры имеют ID из базы данных');
console.log('- [ ] При сохранении выполняется UPDATE');
console.log('- [ ] Позиция сохраняется в базе данных');
console.log('- [ ] После перезагрузки коллайдер остается в новом месте');
console.log('');
console.log('✅ Создание новых:');
console.log('- [ ] Новые коллайдеры помечены как id: null');
console.log('- [ ] При сохранении выполняется INSERT');
console.log('- [ ] Новый коллайдер получает ID в базе данных');
console.log('- [ ] После перезагрузки новый коллайдер загружается');
console.log('');
console.log('✅ Дублирование:');
console.log('- [ ] Дублированные коллайдеры помечены как id: null');
console.log('- [ ] При сохранении выполняется INSERT');
console.log('- [ ] Оригинал обновляется, копия создается');
console.log('- [ ] После перезагрузки оба коллайдера сохраняются');
console.log('');
console.log('✅ Автоматическое сохранение:');
console.log('- [ ] Срабатывает через 2 секунды после изменений');
console.log('- [ ] Правильно обрабатывает существующие и новые коллайдеры');
console.log('- [ ] Логирует количество обновленных и созданных');
console.log('- [ ] Сохраняет все изменения в базе данных');
console.log('');
console.log('🔍 Отладочная информация:');
console.log('');
console.log('📊 В консоли должно появляться:');
console.log('- "💾 Сохраняем: X существующих, Y новых коллайдеров"');
console.log('- "✅ Коллайдеры для города 1 сохранены в БД (X обновлено, Y новых)"');
console.log('- "💾 Автоматическое сохранение выполнено"');
console.log('');
console.log('🎯 Проверка в базе данных:');
console.log('- SELECT * FROM colliders WHERE city_id = 1;');
console.log('- Проверить updated_at для перемещенных коллайдеров');
console.log('- Проверить created_at для новых коллайдеров');
console.log('');
console.log('🚀 Исправления готовы к тестированию!');
console.log('Откройте: http://localhost:4000/enhanced-collision-editor');
console.log('Попробуйте переместить старый коллайдер и сохранить изменения');

View File

@@ -0,0 +1,145 @@
// Тест исправления ошибок редактора коллизий
// Файл: test-collision-editor-error-fixes.js
console.log('🔧 Исправление ошибок редактора коллизий');
console.log('');
console.log('❓ Проблемы из лога:');
console.log('');
console.log('1. 🚨 Ошибка 500 (Internal Server Error):');
console.log(' ПРОБЛЕМА: Сервер возвращал ошибку 500 при загрузке/сохранении');
console.log(' ПРИЧИНА: Таблица colliders не была создана в базе данных');
console.log(' РЕШЕНИЕ: Выполнена миграция для создания таблицы');
console.log('');
console.log('2. 📄 JSON файл не найден:');
console.log(' ПРОБЛЕМА: Фронтенд не мог загрузить коллайдеры');
console.log(' ПРИЧИНА: Новые API endpoints не работали');
console.log(' РЕШЕНИЕ: Добавлен fallback на старые JSON endpoints');
console.log('');
console.log('3. 👻 Не видны старые коллайдеры:');
console.log(' ПРОБЛЕМА: Коллайдеры из интерьера не отображались');
console.log(' ПРИЧИНА: Данные были только в JSON, не в БД');
console.log(' РЕШЕНИЕ: Мигрированы данные из JSON в базу данных');
console.log('');
console.log('✅ Выполненные исправления:');
console.log('');
console.log('1. 🗄️ Миграция базы данных:');
console.log(' - Создана таблица colliders с полной структурой');
console.log(' - Добавлены индексы для производительности');
console.log(' - Созданы триггеры для автоматического обновления');
console.log(' - Настроены внешние ключи для целостности данных');
console.log('');
console.log('2. 🔄 Fallback система:');
console.log(' - При ошибке 500 автоматически переключается на старый JSON API');
console.log(' - Работает как для загрузки, так и для сохранения');
console.log(' - Логирование переключения для отладки');
console.log(' - Обратная совместимость с существующими данными');
console.log('');
console.log('3. 📊 Миграция данных:');
console.log(' - Перенесены все 6 коллайдеров из JSON в БД');
console.log(' - Сохранены все параметры: позиция, поворот, масштаб, цвет, прозрачность');
console.log(' - Транзакционная безопасность миграции');
console.log(' - Проверка целостности данных');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('🗄️ Структура таблицы colliders:');
console.log('- id: SERIAL PRIMARY KEY');
console.log('- city_id: INTEGER (связь с городами)');
console.log('- type: VARCHAR(20) (box, circle, capsule)');
console.log('- position_x/y/z: DECIMAL(15,6) (координаты)');
console.log('- rotation_x/y/z: DECIMAL(15,6) (углы в радианах)');
console.log('- scale_x/y/z: DECIMAL(15,6) (масштаб)');
console.log('- color_r/g/b: DECIMAL(3,2) (RGB 0-1)');
console.log('- opacity: DECIMAL(3,2) (прозрачность 0-1)');
console.log('- created_at/updated_at: TIMESTAMP');
console.log('');
console.log('🔄 Fallback логика:');
console.log('- Сначала пробует новый API: /api/colliders/city/:cityId');
console.log('- При ошибке 500 переключается на старый: /api/colliders?cityId=:cityId');
console.log('- Логирует переключение: "🔄 Новый API недоступен, пробуем старый JSON API..."');
console.log('- Сохраняет обратную совместимость');
console.log('');
console.log('📊 Мигрированные данные:');
console.log('- 6 коллайдеров типа "box"');
console.log('- Различные позиции в интерьере');
console.log('- Цвета: зеленые (0.2, 1, 0.1) и красные (1, 0, 0)');
console.log('- Все параметры трансформации сохранены');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 🔄 Загрузка коллайдеров:');
console.log(' - Откройте редактор коллизий');
console.log(' - Проверьте консоль: должно появиться "✅ Загружено 6 коллайдеров"');
console.log(' - Убедитесь, что коллайдеры видны в 3D сцене');
console.log(' - Проверьте, что они имеют правильные цвета');
console.log('');
console.log('2. 💾 Сохранение коллайдеров:');
console.log(' - Создайте новый коллайдер');
console.log(' - Подождите 2 секунды для автоматического сохранения');
console.log(' - Проверьте консоль: "💾 Автоматическое сохранение выполнено"');
console.log(' - Перезагрузите страницу и убедитесь, что коллайдер сохранился');
console.log('');
console.log('3. 🎯 Выбор коллайдеров:');
console.log(' - Кликните по коллайдеру');
console.log(' - Проверьте консоль: "🎯 Выбран коллайдер"');
console.log(' - Убедитесь, что TransformControls активируется');
console.log(' - Проверьте обновление параметров в UI');
console.log('');
console.log('4. 🔄 Fallback система:');
console.log(' - Временно отключите БД (если возможно)');
console.log(' - Попробуйте загрузить/сохранить коллайдеры');
console.log(' - Проверьте консоль: должно появиться сообщение о переключении');
console.log(' - Убедитесь, что система работает через JSON');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Загрузка:');
console.log('- [ ] Нет ошибок 500 в консоли');
console.log('- [ ] Загружается 6 коллайдеров из БД');
console.log('- [ ] Коллайдеры видны в 3D сцене');
console.log('- [ ] Правильные цвета и позиции');
console.log('');
console.log('✅ Сохранение:');
console.log('- [ ] Автоматическое сохранение работает');
console.log('- [ ] Ручное сохранение работает');
console.log('- [ ] Данные сохраняются в БД');
console.log('- [ ] Нет ошибок при сохранении');
console.log('');
console.log('✅ Управление:');
console.log('- [ ] Выбор коллайдеров работает');
console.log('- [ ] TransformControls активируется');
console.log('- [ ] Параметры обновляются в UI');
console.log('- [ ] Дублирование и удаление работают');
console.log('');
console.log('✅ Fallback:');
console.log('- [ ] При ошибке БД переключается на JSON');
console.log('- [ ] Логируется переключение');
console.log('- [ ] Система остается работоспособной');
console.log('- [ ] Обратная совместимость сохранена');
console.log('');
console.log('🚀 Все исправления готовы к тестированию!');
console.log('Откройте: http://localhost:4000/enhanced-collision-editor');
console.log('Проверьте, что все коллайдеры загружаются и работают корректно');

View File

@@ -0,0 +1,173 @@
// Тест всех улучшений редактора коллизий
// Файл: test-complete-collision-editor-improvements.js
console.log('🚀 Полные улучшения редактора коллизий');
console.log('');
console.log('✅ Решенные проблемы:');
console.log('');
console.log('1. 🔄 Сохранение параметров трансформации:');
console.log(' ПРОБЛЕМА: При перемещении объекты плохо сохранялись');
console.log(' РЕШЕНИЕ: Исправлено обновление данных при изменении через TransformControls');
console.log(' - Добавлено обновление UI параметров при изменении через TransformControls');
console.log(' - Автоматическое обновление colliderPosition, colliderRotation, colliderScale');
console.log(' - Логирование изменений для отладки');
console.log('');
console.log('2. 🗄️ Миграция с JSON на базу данных:');
console.log(' ПРОБЛЕМА: Данные хранились в JSON файлах');
console.log(' РЕШЕНИЕ: Полная миграция на PostgreSQL');
console.log(' - Создана таблица colliders с полной структурой');
console.log(' - Новые API endpoints для работы с БД');
console.log(' - Транзакции для надежности');
console.log(' - Индексы для производительности');
console.log('');
console.log('3. 🎛️ Улучшенное управление:');
console.log(' ПРОБЛЕМА: Управление было неудобным');
console.log(' РЕШЕНИЕ: Значительно улучшен интерфейс');
console.log(' - Автоматическое сохранение с настраиваемой задержкой');
console.log(' - Индикация последнего сохранения');
console.log(' - Удобные кнопки сохранения/загрузки');
console.log(' - Переключение режимов TransformControls');
console.log('');
console.log('4. 💾 Автоматическое сохранение:');
console.log(' НОВАЯ ФУНКЦИЯ: Автоматическое сохранение изменений');
console.log(' - Задержка 2 секунды после изменений');
console.log(' - Отмена предыдущих таймеров при новых изменениях');
console.log(' - Индикация статуса сохранения');
console.log(' - Возможность отключения');
console.log('');
console.log('🔧 Технические улучшения:');
console.log('');
console.log('📊 База данных:');
console.log('- Таблица colliders с полной структурой');
console.log('- Поля для позиции, поворота, масштаба, цвета, прозрачности');
console.log('- Автоматические timestamps (created_at, updated_at)');
console.log('- Индексы для быстрого поиска');
console.log('- Триггеры для автоматического обновления updated_at');
console.log('- Внешние ключи для связи с городами');
console.log('');
console.log('🌐 API Endpoints:');
console.log('- GET /api/colliders/city/:cityId - получение коллайдеров');
console.log('- POST /api/colliders/city/:cityId - сохранение всех коллайдеров');
console.log('- PUT /api/colliders/:colliderId - обновление отдельного коллайдера');
console.log('- DELETE /api/colliders/:colliderId - удаление отдельного коллайдера');
console.log('- Транзакции для надежности операций');
console.log('- Валидация входных данных');
console.log('');
console.log('🎮 Улучшенный интерфейс:');
console.log('- Автоматическое сохранение с индикацией');
console.log('- Переключение режимов TransformControls (перемещение/поворот/масштаб)');
console.log('- Удобные кнопки управления');
console.log('- Отображение времени последнего сохранения');
console.log('- Состояния загрузки/сохранения');
console.log('');
console.log('🧪 Как тестировать улучшения:');
console.log('');
console.log('1. 🗄️ Миграция базы данных:');
console.log(' - Запустите: node migrate-colliders.js');
console.log(' - Проверьте создание таблицы colliders');
console.log(' - Убедитесь в наличии индексов и триггеров');
console.log('');
console.log('2. 🔄 Автоматическое сохранение:');
console.log(' - Создайте или измените коллайдер');
console.log(' - Подождите 2 секунды');
console.log(' - Проверьте консоль: "💾 Автоматическое сохранение выполнено"');
console.log(' - Проверьте время последнего сохранения в UI');
console.log('');
console.log('3. 🎛️ Режимы TransformControls:');
console.log(' - Выберите коллайдер');
console.log(' - Переключайтесь между режимами: Перемещение, Поворот, Масштаб');
console.log(' - Проверьте изменение осей в 3D сцене');
console.log(' - Убедитесь в автоматическом сохранении изменений');
console.log('');
console.log('4. 📊 Работа с базой данных:');
console.log(' - Создайте несколько коллайдеров');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что коллайдеры загружаются из БД');
console.log(' - Проверьте сохранение в БД через API');
console.log('');
console.log('5. 🔧 Удобство управления:');
console.log(' - Используйте кнопки "Сохранить сейчас" и "Перезагрузить"');
console.log(' - Отключите/включите автоматическое сохранение');
console.log(' - Проверьте индикацию состояний загрузки/сохранения');
console.log('');
console.log('🎯 Преимущества улучшений:');
console.log('');
console.log('✅ Надежность:');
console.log('- Транзакции обеспечивают целостность данных');
console.log('- Автоматическое сохранение предотвращает потерю данных');
console.log('- Валидация входных данных');
console.log('- Обработка ошибок');
console.log('');
console.log('✅ Производительность:');
console.log('- Индексы для быстрого поиска');
console.log('- Оптимизированные SQL запросы');
console.log('- Дебаунсинг автоматического сохранения');
console.log('- Эффективное управление состоянием');
console.log('');
console.log('✅ Удобство использования:');
console.log('- Интуитивный интерфейс');
console.log('- Автоматическое сохранение');
console.log('- Визуальная обратная связь');
console.log('- Гибкие настройки');
console.log('');
console.log('✅ Масштабируемость:');
console.log('- База данных вместо файлов');
console.log('- API для интеграции');
console.log('- Структурированные данные');
console.log('- Возможность расширения');
console.log('');
console.log('📋 Чек-лист тестирования:');
console.log('');
console.log('✅ Миграция БД:');
console.log('- [ ] Таблица colliders создана');
console.log('- [ ] Индексы созданы');
console.log('- [ ] Триггеры работают');
console.log('- [ ] Внешние ключи настроены');
console.log('');
console.log('✅ API Endpoints:');
console.log('- [ ] GET /api/colliders/city/:cityId работает');
console.log('- [ ] POST /api/colliders/city/:cityId работает');
console.log('- [ ] PUT /api/colliders/:colliderId работает');
console.log('- [ ] DELETE /api/colliders/:colliderId работает');
console.log('');
console.log('✅ Автоматическое сохранение:');
console.log('- [ ] Сохранение через 2 секунды после изменений');
console.log('- [ ] Отмена предыдущих таймеров');
console.log('- [ ] Индикация времени последнего сохранения');
console.log('- [ ] Возможность отключения');
console.log('');
console.log('✅ Управление:');
console.log('- [ ] Переключение режимов TransformControls');
console.log('- [ ] Автоматическое обновление UI параметров');
console.log('- [ ] Удобные кнопки управления');
console.log('- [ ] Состояния загрузки/сохранения');
console.log('');
console.log('🚀 Все улучшения готовы к тестированию!');
console.log('Откройте: http://localhost:4000/enhanced-collision-editor');
console.log('Сначала выполните миграцию: node migrate-colliders.js');

View File

@@ -0,0 +1,167 @@
// Исправление отображения коллизионных объектов в игре
// Файл: test-game-collider-visibility-fix.js
console.log('🔧 Исправление отображения коллизионных объектов в игре');
console.log('');
console.log('❓ Проблема:');
console.log('Коллизионные объекты до сих пор видны, не смотря на то, что их непрозрачность равна нулю');
console.log('');
console.log('🔍 Причина проблемы:');
console.log('');
console.log('1. 🎮 Разная логика в редакторе и игре:');
console.log(' - В редакторе: коллайдеры используют прозрачность из базы данных');
console.log(' - В игре: коллайдеры создаются с фиксированной прозрачностью 0.001');
console.log(' - Игра не учитывает настройки прозрачности из БД');
console.log('');
console.log('2. 🔧 Фиксированная прозрачность в игре:');
console.log(' - const material = new THREE.MeshBasicMaterial({ opacity: 0.001 })');
console.log(' - Все коллизионные объекты имеют одинаковую прозрачность');
console.log(' - Не учитывается значение opacity из базы данных');
console.log('');
console.log('3. 👁️ Отсутствие логики невидимости:');
console.log(' - В игре нет проверки opacity === 0');
console.log(' - Нет установки material.visible = false');
console.log(' - Нет использования alphaTest для правильной прозрачности');
console.log('');
console.log('✅ Исправления:');
console.log('');
console.log('1. 🎮 Использование прозрачности из базы данных:');
console.log(' - const opacity = c.opacity !== undefined ? c.opacity : 0.001');
console.log(' - Каждый коллайдер использует свою прозрачность из БД');
console.log(' - Fallback на 0.001 для старых коллайдеров');
console.log('');
console.log('2. 👁️ Логика невидимости:');
console.log(' - if (opacity === 0) { material.visible = false; alphaTest = 0; }');
console.log(' - else { material.visible = true; alphaTest = 0.1; }');
console.log(' - Правильная обработка полностью прозрачных объектов');
console.log('');
console.log('3. 🔧 Дополнительные функции для отладки:');
console.log(' - window.updateColliderOpacity(opacity) - обновить прозрачность всех коллайдеров');
console.log(' - window.toggleColliderVisibility(visible) - включить/выключить видимость');
console.log(' - window.reloadAllColliders() - перезагрузить все коллайдеры');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('🎮 Логика создания коллайдеров в игре:');
console.log('- Использование прозрачности из базы данных: c.opacity');
console.log('- Fallback на 0.001 для совместимости');
console.log('- Проверка opacity === 0 для невидимости');
console.log('- Установка material.visible и alphaTest');
console.log('');
console.log('👁️ Обработка прозрачности:');
console.log('- opacity = 0: material.visible = false, alphaTest = 0');
console.log('- opacity > 0: material.visible = true, alphaTest = 0.1');
console.log('- Применяется к каждому коллайдеру индивидуально');
console.log('');
console.log('🔧 Функции отладки:');
console.log('- updateColliderOpacity(opacity): обновить прозрачность всех коллайдеров');
console.log('- toggleColliderVisibility(visible): включить/выключить видимость');
console.log('- reloadAllColliders(): перезагрузить все коллайдеры из БД');
console.log('- checkCollidersInDB(): проверить состояние БД');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 🎮 Тест прозрачности в игре:');
console.log(' - Откройте игру: http://localhost:4000');
console.log(' - Проверьте консоль: window.checkCollidersInDB()');
console.log(' - Убедитесь, что коллайдеры загружены из БД');
console.log(' - Коллайдеры с opacity = 0 должны быть невидимы');
console.log(' - Коллайдеры с opacity > 0 должны быть видимы');
console.log('');
console.log('2. 🔧 Тест функций отладки:');
console.log(' - window.updateColliderOpacity(0) - сделать все невидимыми');
console.log(' - window.updateColliderOpacity(0.5) - сделать полупрозрачными');
console.log(' - window.toggleColliderVisibility(false) - выключить видимость');
console.log(' - window.toggleColliderVisibility(true) - включить видимость');
console.log('');
console.log('3. 🔄 Тест перезагрузки:');
console.log(' - window.reloadAllColliders() - перезагрузить все коллайдеры');
console.log(' - Проверьте, что прозрачность применяется корректно');
console.log(' - Убедитесь, что коллайдеры с opacity = 0 невидимы');
console.log('');
console.log('4. 📊 Тест синхронизации с редактором:');
console.log(' - Откройте редактор коллизий');
console.log(' - Установите прозрачность коллайдера в 0');
console.log(' - Сохраните изменения');
console.log(' - Вернитесь в игру');
console.log(' - Выполните window.reloadAllColliders()');
console.log(' - Коллайдер должен стать невидимым');
console.log('');
console.log('5. 🎯 Тест с разными значениями прозрачности:');
console.log(' - Создайте коллайдеры с разной прозрачностью (0, 0.3, 0.7, 1)');
console.log(' - Сохраните изменения');
console.log(' - Перезагрузите игру');
console.log(' - Проверьте, что каждый коллайдер имеет правильную прозрачность');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Прозрачность в игре:');
console.log('- [ ] Коллайдеры с opacity = 0 полностью невидимы');
console.log('- [ ] Коллайдеры с opacity > 0 видимы с правильной прозрачностью');
console.log('- [ ] Каждый коллайдер использует свою прозрачность из БД');
console.log('- [ ] Fallback на 0.001 для старых коллайдеров');
console.log('');
console.log('✅ Функции отладки:');
console.log('- [ ] window.updateColliderOpacity() работает корректно');
console.log('- [ ] window.toggleColliderVisibility() работает корректно');
console.log('- [ ] window.reloadAllColliders() обновляет прозрачность');
console.log('- [ ] window.checkCollidersInDB() показывает правильные данные');
console.log('');
console.log('✅ Синхронизация с редактором:');
console.log('- [ ] Изменения прозрачности в редакторе отражаются в игре');
console.log('- [ ] Коллайдеры с opacity = 0 невидимы в обоих местах');
console.log('- [ ] Отсутствие рассинхронизации');
console.log('- [ ] Корректная работа автоматического сохранения');
console.log('');
console.log('✅ Производительность:');
console.log('- [ ] Быстрая загрузка коллайдеров с правильной прозрачностью');
console.log('- [ ] Отсутствие задержек при перезагрузке');
console.log('- [ ] Стабильная работа коллизий');
console.log('- [ ] Корректная работа прозрачности');
console.log('');
console.log('🔍 Отладочная информация:');
console.log('');
console.log('📊 В консоли должно появляться:');
console.log('- "📊 Коллайдеры в БД: X штук"');
console.log('- "👁️ Обновляем прозрачность всех коллизионных объектов: Y"');
console.log('- "✅ Прозрачность обновлена для Z коллизионных объектов"');
console.log('- "👁️ Переключаем видимость коллизионных объектов: true/false"');
console.log('- "✅ Видимость коллизионных объектов: включена/выключена"');
console.log('');
console.log('🎯 Проверка в игре:');
console.log('- Коллайдеры с opacity = 0 не должны быть видны');
console.log('- Коллайдеры с opacity > 0 должны быть видны с правильной прозрачностью');
console.log('- Функции отладки должны работать корректно');
console.log('- Перезагрузка должна обновлять прозрачность');
console.log('');
console.log('🚀 Исправления готовы к тестированию!');
console.log('Откройте игру: http://localhost:4000');
console.log('Протестируйте прозрачность коллизионных объектов и функции отладки');

View File

@@ -0,0 +1,153 @@
// Исправление загрузки коллайдеров в игре
// Файл: test-game-colliders-loading.js
console.log('🔧 Исправление загрузки коллайдеров в игре');
console.log('');
console.log('❓ Проблема:');
console.log('Редактор коллизий работает с базой данных, но сама игра загружает коллайдеры из JSON файла');
console.log('Коллайдеры, созданные в редакторе, не отображаются в игре');
console.log('');
console.log('🔍 Причина проблемы:');
console.log('');
console.log('1. 🔄 Разные источники данных:');
console.log(' - Редактор коллизий: загружает из /api/colliders/city/:cityId (база данных)');
console.log(' - Игра: загружает из /colliders_city_1.json (JSON файл)');
console.log(' - Данные не синхронизируются между источниками');
console.log('');
console.log('2. 📊 Две функции загрузки в Game.js:');
console.log(' - loadCollidersFromJSON(): для коллизионных данных (Box3)');
console.log(' - loadCustomCollidersForCity(): для визуальных коллайдеров (Mesh)');
console.log(' - Обе использовали только JSON файлы');
console.log('');
console.log('3. 🗄️ Отсутствие fallback механизма:');
console.log(' - Игра не могла загрузить данные из базы данных');
console.log(' - Нет резервного варианта при недоступности БД');
console.log('');
console.log('✅ Исправления:');
console.log('');
console.log('1. 🔄 Обновление loadCollidersFromJSON():');
console.log(' - Сначала пробует загрузить из /api/colliders/city/:cityId');
console.log(' - При 500 ошибке fallback на /colliders_city_1.json');
console.log(' - Обрабатывает данные из обоих источников');
console.log('');
console.log('2. 🔄 Обновление loadCustomCollidersForCity():');
console.log(' - Сначала пробует загрузить из /api/colliders/city/:cityId');
console.log(' - При 500 ошибке fallback на /api/colliders?cityId=:cityId');
console.log(' - Создает визуальные меши для коллайдеров');
console.log('');
console.log('3. 📊 Улучшенная обработка данных:');
console.log(' - Проверка формата данных (data.colliders vs Array)');
console.log(' - Логирование источника данных');
console.log(' - Отладочная информация для диагностики');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('🔄 Логика загрузки:');
console.log('- Первая попытка: /api/colliders/city/:cityId (база данных)');
console.log('- Fallback: JSON файлы или старый API');
console.log('- Проверка ответа: response.ok && response.status !== 500');
console.log('- Обработка данных: data.colliders || data (массив)');
console.log('');
console.log('📊 Два типа коллайдеров в игре:');
console.log('- Коллизионные (Box3): для проверки столкновений');
console.log('- Визуальные (Mesh): для отображения в сцене');
console.log('- Оба типа теперь загружаются из базы данных');
console.log('');
console.log('🗄️ API endpoints:');
console.log('- Новый: GET /api/colliders/city/:cityId (база данных)');
console.log('- Старый: GET /api/colliders?cityId=:cityId (JSON файлы)');
console.log('- Fallback: GET /colliders_city_1.json (прямые файлы)');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 🎮 Запуск игры:');
console.log(' - Откройте игру: http://localhost:4000');
console.log(' - Проверьте консоль браузера');
console.log(' - Должно появиться: "📊 Загружены коллайдеры из базы данных"');
console.log(' - Или: "📄 Загружены коллайдеры из JSON файла" (fallback)');
console.log('');
console.log('2. 🔍 Проверка коллайдеров:');
console.log(' - Коллайдеры должны быть видны в игре');
console.log(' - Коллизии должны работать корректно');
console.log(' - Проверьте, что персонаж не проходит через коллайдеры');
console.log('');
console.log('3. 🔄 Синхронизация с редактором:');
console.log(' - Создайте коллайдер в редакторе коллизий');
console.log(' - Сохраните изменения');
console.log(' - Перезагрузите игру');
console.log(' - Новый коллайдер должен появиться в игре');
console.log('');
console.log('4. 🗄️ Тест fallback:');
console.log(' - Остановите сервер базы данных');
console.log(' - Перезагрузите игру');
console.log(' - Должно появиться: "🔄 Новый API недоступен, пробуем старый JSON API"');
console.log(' - Игра должна загрузиться с данными из JSON файлов');
console.log('');
console.log('5. 📊 Отладочная информация:');
console.log(' - Проверьте консоль на наличие ошибок');
console.log(' - Должны быть логи о загрузке коллайдеров');
console.log(' - Проверьте количество загруженных коллайдеров');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Загрузка из базы данных:');
console.log('- [ ] Игра загружает коллайдеры из БД');
console.log('- [ ] Коллайдеры из редактора видны в игре');
console.log('- [ ] Коллизии работают корректно');
console.log('- [ ] Синхронизация между редактором и игрой');
console.log('');
console.log('✅ Fallback механизм:');
console.log('- [ ] При недоступности БД загружаются JSON файлы');
console.log('- [ ] Игра работает в любом случае');
console.log('- [ ] Логирование источника данных');
console.log('- [ ] Отсутствие критических ошибок');
console.log('');
console.log('✅ Производительность:');
console.log('- [ ] Быстрая загрузка коллайдеров');
console.log('- [ ] Отсутствие задержек в игре');
console.log('- [ ] Корректная работа коллизий');
console.log('- [ ] Стабильная работа при переключении городов');
console.log('');
console.log('🔍 Отладочная информация:');
console.log('');
console.log('📊 В консоли должно появляться:');
console.log('- "🔍 loadCollidersFromJSON вызвана для города: 1"');
console.log('- "📊 Загружены коллайдеры из базы данных: X объектов"');
console.log('- "🔍 loadCustomCollidersForCity для города: 1"');
console.log('- "📊 Загружены кастомные коллайдеры из базы данных: X объектов"');
console.log('');
console.log('🎯 Проверка в игре:');
console.log('- Коллайдеры должны быть видны (если включена визуализация)');
console.log('- Персонаж не должен проходить через коллайдеры');
console.log('- Коллизии должны работать плавно');
console.log('');
console.log('🚀 Исправления готовы к тестированию!');
console.log('Откройте игру: http://localhost:4000');
console.log('Проверьте консоль и убедитесь, что коллайдеры загружаются из базы данных');

View File

@@ -0,0 +1,176 @@
// Исправление проблем с прозрачностью и коллизиями
// Файл: test-opacity-collision-fixes.js
console.log('🔧 Исправление проблем с прозрачностью и коллизиями');
console.log('');
console.log('❓ Проблемы:');
console.log('1) Даже когда у коллайдера прозрачность 0, всё равно его видно');
console.log('2) В старом json был объект, сейчас я удалил, его не видно, но при этом сквозь него я всё равно пройти не могу');
console.log('');
console.log('🔍 Причины проблем:');
console.log('');
console.log('1. 👁️ Проблема с прозрачностью:');
console.log(' - В Three.js opacity = 0 не делает объект полностью невидимым');
console.log(' - Нужно дополнительно установить visible = false');
console.log(' - Или использовать alphaTest для правильной обработки прозрачности');
console.log('');
console.log('2. 🚫 Проблема с коллизиями удаленных коллайдеров:');
console.log(' - Игра загружает коллайдеры из разных источников');
console.log(' - Коллизионные коллайдеры (Box3) и визуальные (Mesh) загружаются отдельно');
console.log(' - При удалении коллайдера из редактора игра может не перезагрузить коллайдеры');
console.log(' - Кэширование или рассинхронизация между источниками');
console.log('');
console.log('✅ Исправления:');
console.log('');
console.log('1. 👁️ Исправление прозрачности:');
console.log(' - Добавлена проверка opacity === 0');
console.log(' - При opacity = 0: material.visible = false, alphaTest = 0');
console.log(' - При opacity > 0: material.visible = true, alphaTest = 0.1');
console.log(' - Обновлены все функции создания и изменения коллайдеров');
console.log('');
console.log('2. 🚫 Исправление коллизий удаленных коллайдеров:');
console.log(' - Добавлена функция window.reloadAllColliders() в игре');
console.log(' - Добавлена функция window.checkCollidersInDB() для диагностики');
console.log(' - Перезагрузка как коллизионных, так и визуальных коллайдеров');
console.log(' - Сравнение состояния БД с состоянием игры');
console.log('');
console.log('3. 🔄 Улучшенная синхронизация:');
console.log(' - Функция reloadColliders в редакторе');
console.log(' - Кнопка "🔄 Синхронизировать" в UI редактора');
console.log(' - Глобальные функции для отладки в игре');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('👁️ Логика прозрачности:');
console.log('- if (opacity === 0) { material.visible = false; alphaTest = 0; }');
console.log('- else { material.visible = true; alphaTest = 0.1; }');
console.log('- Применяется к colliderMaterial, загруженным коллайдерам, изменениям в UI');
console.log('');
console.log('🚫 Логика перезагрузки коллайдеров:');
console.log('- loadCollidersFromJSON(1) - коллизионные коллайдеры (Box3)');
console.log('- loadCustomCollidersForCity(1) - визуальные коллайдеры (Mesh)');
console.log('- window.reloadAllColliders() - перезагрузка всех типов');
console.log('- window.checkCollidersInDB() - диагностика состояния');
console.log('');
console.log('🔄 Источники коллайдеров в игре:');
console.log('- Коллизионные: jsonCollidersRef.current (Box3 объекты)');
console.log('- Визуальные: visualCollidersRef.current (Mesh объекты)');
console.log('- Оба загружаются из /api/colliders/city/1 с fallback на JSON');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 👁️ Тест прозрачности:');
console.log(' - Откройте редактор коллизий');
console.log(' - Выберите коллайдер');
console.log(' - Установите прозрачность в 0');
console.log(' - Нажмите "Применить цвет"');
console.log(' - Коллайдер должен стать полностью невидимым');
console.log(' - Установите прозрачность обратно в 0.3');
console.log(' - Коллайдер должен стать видимым');
console.log('');
console.log('2. 🚫 Тест коллизий удаленных коллайдеров:');
console.log(' - Откройте игру');
console.log(' - Проверьте консоль: window.checkCollidersInDB()');
console.log(' - Убедитесь, что коллайдеры загружены из БД');
console.log(' - Откройте редактор коллизий');
console.log(' - Удалите коллайдер');
console.log(' - Вернитесь в игру');
console.log(' - Выполните: window.reloadAllColliders()');
console.log(' - Проверьте, что коллизии исчезли');
console.log('');
console.log('3. 🔄 Тест синхронизации:');
console.log(' - В редакторе: нажмите "🔄 Синхронизировать"');
console.log(' - В игре: выполните window.reloadAllColliders()');
console.log(' - Проверьте консоль на наличие ошибок');
console.log(' - Убедитесь, что количество коллайдеров совпадает');
console.log('');
console.log('4. 📊 Диагностика:');
console.log(' - В игре: window.checkCollidersInDB()');
console.log(' - Проверьте количество коллайдеров в БД vs в игре');
console.log(' - Убедитесь, что данные синхронизированы');
console.log('');
console.log('5. 🎯 Тест с несколькими коллайдерами:');
console.log(' - Создайте несколько коллайдеров с разной прозрачностью');
console.log(' - Удалите некоторые из них');
console.log(' - Сохраните изменения');
console.log(' - Перезагрузите игру');
console.log(' - Выполните window.reloadAllColliders()');
console.log(' - Проверьте, что только нужные коллайдеры остались');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Прозрачность:');
console.log('- [ ] Коллайдеры с opacity = 0 полностью невидимы');
console.log('- [ ] Коллайдеры с opacity > 0 видимы с правильной прозрачностью');
console.log('- [ ] Изменения прозрачности применяются мгновенно');
console.log('- [ ] Сохранение прозрачности работает корректно');
console.log('');
console.log('✅ Коллизии удаленных коллайдеров:');
console.log('- [ ] Удаленные коллайдеры не создают коллизий');
console.log('- [ ] window.reloadAllColliders() обновляет все коллайдеры');
console.log('- [ ] Синхронизация между редактором и игрой');
console.log('- [ ] Отсутствие "призрачных" коллизий');
console.log('');
console.log('✅ Синхронизация:');
console.log('- [ ] Кнопка "Синхронизировать" в редакторе работает');
console.log('- [ ] window.reloadAllColliders() в игре работает');
console.log('- [ ] window.checkCollidersInDB() показывает правильные данные');
console.log('- [ ] Отсутствие рассинхронизации');
console.log('');
console.log('✅ Производительность:');
console.log('- [ ] Быстрая перезагрузка коллайдеров');
console.log('- [ ] Отсутствие задержек в игре');
console.log('- [ ] Стабильная работа коллизий');
console.log('- [ ] Корректная работа прозрачности');
console.log('');
console.log('🔍 Отладочная информация:');
console.log('');
console.log('📊 В консоли должно появляться:');
console.log('- "🔄 Принудительная перезагрузка всех коллайдеров"');
console.log('- "✅ Коллизионные коллайдеры перезагружены"');
console.log('- "✅ Визуальные коллайдеры перезагружены"');
console.log('- "📊 Коллайдеры в БД: X штук"');
console.log('- "📊 Коллизионные коллайдеры в игре: Y штук"');
console.log('');
console.log('🎯 Проверка прозрачности:');
console.log('- Коллайдеры с opacity = 0 не должны быть видны');
console.log('- Коллайдеры с opacity > 0 должны быть видны с правильной прозрачностью');
console.log('- Изменения должны применяться мгновенно');
console.log('');
console.log('🎯 Проверка коллизий:');
console.log('- Удаленные коллайдеры не должны создавать коллизий');
console.log('- Персонаж должен проходить через места, где были удаленные коллайдеры');
console.log('- Коллизии должны работать только для существующих коллайдеров');
console.log('');
console.log('🚀 Исправления готовы к тестированию!');
console.log('Откройте редактор: http://localhost:4000/enhanced-collision-editor');
console.log('Откройте игру: http://localhost:4000');
console.log('Протестируйте прозрачность и коллизии удаленных коллайдеров');

164
test-wireframe-fix.js Normal file
View File

@@ -0,0 +1,164 @@
// Исправление проблемы с рамками коллайдеров
// Файл: test-wireframe-fix.js
console.log('🔧 Исправление проблемы с рамками коллайдеров');
console.log('');
console.log('❓ Проблема:');
console.log('У каждого коллайдера два объекта: один только рамка, другой полностью закрашен');
console.log('При перемещении только рамки не сохраняется перемещение');
console.log('При перемещении полностью закрашенного объекта всё сохраняется');
console.log('');
console.log('🔍 Причина проблемы:');
console.log('');
console.log('1. 🏗️ Структура коллайдеров:');
console.log(' - Каждый коллайдер состоит из основного меша (mesh) и рамки (line)');
console.log(' - Рамка добавляется как дочерний объект: mesh.add(line)');
console.log(' - При клике можно выбрать либо основной меш, либо рамку');
console.log('');
console.log('2. 🎯 Проблема выбора:');
console.log(' - Рамка (LineSegments) не имеет userData с параметрами коллайдера');
console.log(' - Только основной меш имеет правильные userData и обновляется в collidersRef');
console.log(' - При выборе рамки TransformControls прикрепляется к ней, но данные не сохраняются');
console.log('');
console.log('3. 💾 Проблема сохранения:');
console.log(' - updateColliderData работает только с основными мешами');
console.log(' - Рамки не участвуют в процессе сохранения');
console.log(' - Перемещение рамки не влияет на позицию основного меша');
console.log('');
console.log('✅ Исправления:');
console.log('');
console.log('1. 🎯 Улучшенная логика выбора:');
console.log(' - Raycaster теперь проверяет все объекты коллайдеров (меши + рамки)');
console.log(' - При клике по рамке автоматически выбирается родительский меш');
console.log(' - Проверка, что выбранный объект действительно является коллайдером');
console.log('');
console.log('2. 🔲 Опция отключения рамок:');
console.log(' - Новое состояние showWireframes для управления видимостью');
console.log(' - Функция toggleWireframes для переключения видимости');
console.log(' - Кнопка в UI для удобного управления');
console.log('');
console.log('3. 🏗️ Обновление создания коллайдеров:');
console.log(' - Новые коллайдеры учитывают настройку видимости рамок');
console.log(' - Дублированные коллайдеры учитывают настройку видимости рамок');
console.log(' - Загруженные коллайдеры учитывают настройку видимости рамок');
console.log('');
console.log('🔧 Технические детали:');
console.log('');
console.log('🎯 Логика выбора:');
console.log('- allColliderObjects = [основные меши, рамки]');
console.log('- if (clickedObject.type === "LineSegments") clickedObject = clickedObject.parent');
console.log('- Проверка через collidersRef.current.find(c => c.mesh === clickedObject)');
console.log('');
console.log('🔲 Управление рамками:');
console.log('- showWireframes: boolean состояние');
console.log('- toggleWireframes(): переключает видимость всех рамок');
console.log('- line.visible = showWireframes при создании');
console.log('- child.visible = newShowWireframes при переключении');
console.log('');
console.log('🏗️ Структура коллайдера:');
console.log('- mesh (основной меш с материалом)');
console.log('- mesh.children[0] (рамка LineSegments)');
console.log('- mesh.userData (параметры коллайдера)');
console.log('- collidersRef содержит ссылки на основные меши');
console.log('');
console.log('🧪 Как тестировать исправления:');
console.log('');
console.log('1. 🎯 Выбор коллайдеров:');
console.log(' - Загрузите редактор коллизий');
console.log(' - Попробуйте кликнуть по рамке коллайдера');
console.log(' - Проверьте консоль: "🎯 Кликнули по рамке, выбираем родительский меш"');
console.log(' - Убедитесь, что TransformControls прикрепился к основному мешу');
console.log(' - Переместите коллайдер и сохраните');
console.log(' - Перезагрузите страницу и проверьте, что позиция сохранилась');
console.log('');
console.log('2. 🔲 Переключение рамок:');
console.log(' - Нажмите кнопку "🔲 Скрыть рамки"');
console.log(' - Проверьте консоль: "🔲 Рамки отключены"');
console.log(' - Убедитесь, что все рамки исчезли');
console.log(' - Нажмите кнопку "🔲 Показать рамки"');
console.log(' - Проверьте консоль: "🔲 Рамки включены"');
console.log(' - Убедитесь, что все рамки появились');
console.log('');
console.log('3. 🆕 Создание с отключенными рамками:');
console.log(' - Отключите рамки');
console.log(' - Создайте новый коллайдер');
console.log(' - Убедитесь, что новый коллайдер создался без рамки');
console.log(' - Включите рамки');
console.log(' - Убедитесь, что рамка появилась у нового коллайдера');
console.log('');
console.log('4. 🔄 Дублирование с отключенными рамками:');
console.log(' - Отключите рамки');
console.log(' - Дублируйте существующий коллайдер');
console.log(' - Убедитесь, что дублированный коллайдер создался без рамки');
console.log(' - Включите рамки');
console.log(' - Убедитесь, что рамка появилась у дублированного коллайдера');
console.log('');
console.log('5. 💾 Сохранение перемещений:');
console.log(' - Кликните по рамке коллайдера');
console.log(' - Переместите его в новое место');
console.log(' - Нажмите "Сохранить сейчас"');
console.log(' - Перезагрузите страницу');
console.log(' - Убедитесь, что коллайдер остался в новом месте');
console.log('');
console.log('🎯 Ожидаемые результаты:');
console.log('');
console.log('✅ Выбор коллайдеров:');
console.log('- [ ] Клик по рамке выбирает основной меш');
console.log('- [ ] TransformControls прикрепляется к основному мешу');
console.log('- [ ] Перемещение рамки перемещает весь коллайдер');
console.log('- [ ] Сохранение работает корректно');
console.log('');
console.log('✅ Управление рамками:');
console.log('- [ ] Кнопка переключает видимость всех рамок');
console.log('- [ ] Новые коллайдеры учитывают настройку');
console.log('- [ ] Дублированные коллайдеры учитывают настройку');
console.log('- [ ] Загруженные коллайдеры учитывают настройку');
console.log('');
console.log('✅ Сохранение перемещений:');
console.log('- [ ] Перемещение через рамку сохраняется');
console.log('- [ ] Перемещение через основной меш сохраняется');
console.log('- [ ] Автоматическое сохранение работает');
console.log('- [ ] Ручное сохранение работает');
console.log('');
console.log('🔍 Отладочная информация:');
console.log('');
console.log('📊 В консоли должно появляться:');
console.log('- "🎯 Кликнули по рамке, выбираем родительский меш"');
console.log('- "🔲 Рамки включены/отключены"');
console.log('- "🎯 Выбран коллайдер: [объект]"');
console.log('');
console.log('🎯 Проверка структуры:');
console.log('- selected.type должен быть "Mesh" (не "LineSegments")');
console.log('- selected.userData должен содержать параметры коллайдера');
console.log('- selected.children[0].type должен быть "LineSegments"');
console.log('');
console.log('🚀 Исправления готовы к тестированию!');
console.log('Откройте: http://localhost:4000/enhanced-collision-editor');
console.log('Попробуйте кликнуть по рамке коллайдера и переместить его');