using System.Collections.Generic; using System.Diagnostics; using System.Xml; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate; using Microsoft.Xna.Framework.Graphics; using SerializedData; namespace BouncyCatIsBouncy { class Editor { enum Direction { Height, Width, } List m_obbs = new List(); List m_rbbs = new List(); RadialBB m_cursor = new RadialBB(3.0f); SpriteBatch m_batch; Input m_input; int m_selectedOBB = -1; int m_selectedRBB = -1; bool m_needsSync = true; Physics m_simulator; public Editor(Physics simulator, SpriteBatch batch, Input input) { Debug.Assert(simulator != null); Debug.Assert(batch != null); Debug.Assert(input != null); m_simulator = simulator; m_batch = batch; m_input = input; } public Editor(Physics simulator, SpriteBatch batch, Input input, List existingOBBs, List existingRBBs) : this(simulator, batch, input) { if (existingOBBs != null) m_obbs.AddRange(existingOBBs); if (existingRBBs != null) m_rbbs.AddRange(existingRBBs); } private void FindSelectedBB() { m_selectedOBB = m_selectedRBB = -1; for (var i = 0; i < m_obbs.Count; i++) { if (m_obbs[i].Collides(m_cursor).Collides) { m_selectedOBB = i; return; } } for (var i = 0; i < m_rbbs.Count; i++) { if (m_rbbs[i].Collides(m_cursor).Collides) { m_selectedRBB = i; return; } } } private void Save() { var settings = new XmlWriterSettings(); settings.Indent = true; var data = new LevelData() { OBBs = new List(), RBBs = new List() }; foreach (var obb in m_obbs) { data.OBBs.Add(new OBBData() { Width = obb.Width, Height = obb.Height, Center = obb.Center, Rotation = obb.Rotation, Sound = "", Solid = true }); } foreach (var rbb in m_rbbs) { data.RBBs.Add(new RadialBBData() { Radius = rbb.Radius, Center = rbb.Center, Sound = "", Solid = true }); } using (var writer = XmlWriter.Create(@"LevelData.xml", settings)) { IntermediateSerializer.Serialize(writer, data, null); } } private void Load() { using (var reader = XmlReader.Create(@"LevelData.xml")) { var data = IntermediateSerializer.Deserialize(reader, null); m_obbs.Clear(); foreach (var obb in data.OBBs) { m_obbs.Add(new OBB(obb.Width, obb.Height, obb.Rotation, obb.Center)); } m_rbbs.Clear(); foreach (var rbb in data.RBBs) { m_rbbs.Add(new RadialBB(rbb.Radius, rbb.Center)); } m_needsSync = true; } } private void CreateWallIfNotExists(float width, float height, float rotation, Vector2 center) { bool found = false; foreach (var obb in m_obbs) { if (obb.Height == height && obb.Width == width && obb.Rotation == rotation && obb.Center == center) { found = true; break; } } if (!found) { OBB obb; SolidArea.Create(m_simulator, obb = new OBB(width, height, rotation, center)); m_obbs.Add(obb); } } private void CreateBoundingWalls() { const float WIDTH = 1280.0f; const float HEIGHT = 720.0f; const float THICKNESS = 30.0f; //Left wall CreateWallIfNotExists(THICKNESS, HEIGHT, 0.0f, new Vector2(-THICKNESS / 2.0f, HEIGHT / 2.0f)); //Right wall CreateWallIfNotExists(THICKNESS, HEIGHT, 0.0f, new Vector2(WIDTH + THICKNESS / 2.0f, HEIGHT / 2.0f)); //Bottom wall CreateWallIfNotExists(WIDTH, THICKNESS, 0.0f, new Vector2(WIDTH / 2.0f, HEIGHT + THICKNESS / 2.0f)); //Top wall CreateWallIfNotExists(WIDTH, THICKNESS, 0.0f, new Vector2(WIDTH / 2.0f, -THICKNESS / 2.0f)); } public void SyncWithSimulator() { m_simulator.ClearAllSecondaries(); foreach (var obb in m_obbs) { SolidArea.Create(m_simulator, obb); } foreach (var rbb in m_rbbs) { SolidArea.Create(m_simulator, rbb); } CreateBoundingWalls(); m_needsSync = false; } private float GetRotationAmount() { const float ROUGH_ROTATE_AMOUNT = .025f; const float FINE_ROTATE_AMOUNT = ROUGH_ROTATE_AMOUNT / 10.0f; if (m_input.IsActivityDown(Activity.Editor_FineChange)) return FINE_ROTATE_AMOUNT; return ROUGH_ROTATE_AMOUNT; } private float GetSizeAdjustmentAmount() { const float ROUGH_SIZE_AMOUNT = 5.0f; const float FINE_SIZE_AMOUNT = ROUGH_SIZE_AMOUNT / 10.0f; if (m_input.IsActivityDown(Activity.Editor_FineChange)) return FINE_SIZE_AMOUNT; return ROUGH_SIZE_AMOUNT; } private void RotateSelected(float amount) { if (m_selectedOBB != -1) { OBB obb = m_obbs[m_selectedOBB]; obb.Rotation += amount; m_obbs[m_selectedOBB] = obb; m_needsSync = true; } } private void PositionSelected(Vector2 position) { if (m_selectedOBB != -1) { var obb = m_obbs[m_selectedOBB]; obb.Center = position; m_obbs[m_selectedOBB] = obb; m_needsSync = true; } if (m_selectedRBB != -1) { var rbb = m_rbbs[m_selectedRBB]; rbb.Center = position; m_rbbs[m_selectedRBB] = rbb; m_needsSync = true; } } private void AdjustSelectedSize(float amount, Direction direction) { if (m_selectedOBB != -1) { var obb = m_obbs[m_selectedOBB]; switch (direction) { case Direction.Height: obb.Height += amount; break; case Direction.Width: obb.Width += amount; break; } m_obbs[m_selectedOBB] = obb; m_needsSync = true; } if (m_selectedRBB != -1) { var rbb = m_rbbs[m_selectedRBB]; rbb.Radius += amount; m_rbbs[m_selectedRBB] = rbb; m_needsSync = true; } } private void DeleteSelected() { if (m_selectedOBB != -1) { m_obbs.RemoveAt(m_selectedOBB); m_selectedOBB = -1; m_needsSync = true; } if (m_selectedRBB != -1) { m_rbbs.RemoveAt(m_selectedRBB); m_selectedRBB = -1; m_needsSync = true; } } public void Update(float dt) { m_cursor.Center = m_input.Cursor2DLocation; if (!m_isInteracting) { if (m_input.IsActivityPressed(Activity.Editor_NewOBB)) { var obb = new OBB(100.0f, 100.0f, 0.0f); obb.Center = m_cursor.Center; m_obbs.Add(obb); m_needsSync = true; } if (m_input.IsActivityPressed(Activity.Editor_NewRadialBB)) { var rbb = new RadialBB(25.0f); rbb.Center = m_cursor.Center; m_rbbs.Add(rbb); m_needsSync = true; } FindSelectedBB(); if (m_input.IsActivityDown(Activity.Editor_IncreaseRot)) RotateSelected(GetRotationAmount()); if (m_input.IsActivityDown(Activity.Editor_DecreaseRot)) RotateSelected(-GetRotationAmount()); if (m_input.IsActivityDown(Activity.Editor_IncreaseWidth)) AdjustSelectedSize(GetSizeAdjustmentAmount(), Direction.Width); if (m_input.IsActivityDown(Activity.Editor_DecreaseWidth)) AdjustSelectedSize(-GetSizeAdjustmentAmount(), Direction.Width); if (m_input.IsActivityDown(Activity.Editor_IncreaseHeight)) AdjustSelectedSize(GetSizeAdjustmentAmount(), Direction.Height); if (m_input.IsActivityDown(Activity.Editor_DecreaseHeight)) AdjustSelectedSize(-GetSizeAdjustmentAmount(), Direction.Height); if (m_input.IsActivityPressed(Activity.Editor_Delete)) DeleteSelected(); if (m_input.IsActivityPressed(Activity.Editor_Save)) Save(); if (m_input.IsActivityPressed(Activity.Editor_Load)) Load(); } InteractWithSelected(); if (m_needsSync) SyncWithSimulator(); } private bool HasSelected() { return m_selectedOBB != -1 || m_selectedRBB != -1; } private ICollidable GetSelected() { if (m_selectedOBB != -1) return m_obbs[m_selectedOBB]; if (m_selectedRBB != -1) return m_rbbs[m_selectedRBB]; return null; } Vector2 m_offset; bool m_isInteracting; private void InteractWithSelected() { if (m_isInteracting) { if (HasSelected() && m_input.IsActivityDown(Activity.Editor_Interact)) { PositionSelected(m_input.Cursor2DLocation + m_offset); } else { m_isInteracting = false; } } else if (HasSelected() && m_input.IsActivityDown(Activity.Editor_Interact)) { m_isInteracting = true; m_offset = GetSelected().Center - m_input.Cursor2DLocation; } } } }