/// <summary>Converts a rectangle in simulation space to screen space.</summary> /// <param name="rectangle">The rectangle.</param> /// <returns></returns> public static FarRectangle ToScreenUnits(FarRectangle rectangle) { rectangle.Inflate( rectangle.Width / 2f * (Util.UnitConversion.ScreenOverSimulationRatio - 1f), rectangle.Height / 2f * (Util.UnitConversion.ScreenOverSimulationRatio - 1f)); return(rectangle); }
/// <summary>Converts a rectangle in screen space to simulation space.</summary> /// <param name="rectangle">The rectangle.</param> /// <returns></returns> public static FarRectangle ToSimulationUnits(FarRectangle rectangle) { rectangle.Inflate( -rectangle.Width / 2f * (1f - Util.UnitConversion.SimulationOverScreenRatio), -rectangle.Height / 2f * (1f - Util.UnitConversion.SimulationOverScreenRatio)); return(rectangle); }
// TODO in case we need this somewhere else it might be a good idea to move this to the EntityFactory private void CreateAsteroid(FarPosition position, IUniformRandom random, ContentManager content) { // Randomly scale and rotate it. var scale = (float)random.NextDouble(0.5f, 1f); var angle = (float)random.NextDouble() * MathHelper.TwoPi; // Determine shape for physics system. var textureName = "Textures/Asteroids/rock_" + random.NextInt32(1, 14); var texture = content.Load <Texture2D>(textureName); var hull = new List <Vector2>(TextureConverter.DetectVertices(texture, 8f, textureName: textureName)[0]); for (var k = 0; k < hull.Count; ++k) { hull[k] -= new Vector2(texture.Width / 2f, texture.Height / 2f); hull[k] = XnaUnitConversion.ToSimulationUnits(hull[k]) * scale; } var polygons = EarClipDecomposer.ConvexPartition(hull); // Create physical representation. var entity = Manager.AddEntity(); var body = Manager.AddBody(entity, position, angle, Body.BodyType.Dynamic); foreach (var polygon in polygons) { Manager.AttachPolygon(body, polygon, density: 1000f, restitution: 0.2f); } // Slow down to allow reaching sleep state again. body.LinearDamping = 0.05f * Space.Util.Settings.TicksPerSecond; body.AngularDamping = 0.025f * Space.Util.Settings.TicksPerSecond; // Bounds of the asteroid for rendering culling. We use the diagonal for a loose fit that // contains every possible rotated state of the texture. var width = UnitConversion.ToSimulationUnits(texture.Width); var height = UnitConversion.ToSimulationUnits(texture.Height); var diagonal = (float)Math.Sqrt(width * width + height * height); var bounds = new FarRectangle(-diagonal / 2, -diagonal / 2, diagonal, diagonal); // Rendering stuff. Manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); Manager.AddComponent <Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId); Manager.AddComponent <SimpleTextureDrawable>(entity).Initialize(textureName, scale); // Auto removal. Manager.AddComponent <CellDeath>(entity).Initialize(true); Manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); // Make it destructible. var health = Manager.AddComponent <Health>(entity); health.Value = health.MaxValue = 200 * scale; health.Regeneration = 0f; // As they don't move on their own, start asteroids as sleeping to save performance. body.IsAwake = false; }
public static FarRectangle NextRectangle(this IUniformRandom random, float area, float minSize, float maxSize) { var rect = new FarRectangle { Width = (float)random.NextDouble(minSize, maxSize), Height = (float)random.NextDouble(minSize, maxSize) }; rect.X = (float)random.NextDouble(-area / 2, area / 2 - (int)rect.Width); rect.Y = (float)random.NextDouble(-area / 2, area / 2 - (int)rect.Height); return(rect); }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager">The manager.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public int Sample(IManager manager, IUniformRandom random) { var entity = manager.AddEntity(); // Sample all values in advance, to allow reshuffling component creation // order in case we need to, without influencing the 'random' results. var radius = UnitConversion.ToSimulationUnits(SampleRadius(random)); var rotationSpeed = SampleRotationSpeed(random); var mass = SampleMass(random); var surfaceRotation = random != null ? (Math.Sign(random.NextDouble() - 0.5) * rotationSpeed) : rotationSpeed; // Give it a position. manager.AddComponent <Transform>(entity); // Add it to some indexes. var bounds = new FarRectangle(-radius, -radius, radius * 2, radius * 2); // Can be detected. manager.AddComponent <Indexable>(entity).Initialize(bounds, Detectable.IndexId); // Can make some noise. manager.AddComponent <Indexable>(entity).Initialize(SoundSystem.IndexId); // Must be detectable by the camera. manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Remove when out of bounds or large containing cell dies. manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); manager.AddComponent <CellDeath>(entity).Initialize(false); // Make it rotate. manager.AddComponent <Velocity>(entity).Initialize( Vector2.Zero, MathHelper.ToRadians(rotationSpeed) / Settings.TicksPerSecond); // Make it attract stuff if it has mass. if (mass > 0) { manager.AddComponent <Gravitation>(entity).Initialize(Gravitation.GravitationTypes.Attractor, mass); } // Make it detectable. manager.AddComponent <Detectable>(entity).Initialize("Textures/Radar/Icons/radar_planet"); // Make it visible. manager.AddComponent <PlanetRenderer>(entity).Initialize(this, UnitConversion.ToScreenUnits(radius), surfaceRotation); // Let it rap. manager.AddComponent <Sound>(entity).Initialize("Planet"); return(entity); }
/// <summary>Returns the current bounds of the viewport, i.e. the rectangle of the world to actually render.</summary> /// <returns>The visible bounds, in world coordinates.</returns> public FarRectangle ComputeVisibleBounds() { var center = CameraPosition; var zoom = Zoom; var width = UnitConversion.ToSimulationUnits(_graphics.Viewport.Width / zoom); var height = UnitConversion.ToSimulationUnits(_graphics.Viewport.Height / zoom); // Get scaled viewport bounds, translated to camera position. var result = new FarRectangle { X = (center.X - (width * 0.5f)), Y = (center.Y - (height * 0.5f)), Width = width, Height = height }; return(result); }
/// <summary>Reset this behavior so it can be reused later on.</summary> public override void Reset() { base.Reset(); Area = FarRectangle.Empty; }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager">The manager.</param> /// <param name="cellCenter">The center of the cell the sun will be inserted in.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public int Sample(IManager manager, FarPosition cellCenter, IUniformRandom random) { var entity = manager.AddEntity(); // Sample all values in advance, to allow reshuffling component creation // order in case we need to, without influencing the 'random' results. var radius = UnitConversion.ToSimulationUnits(SampleRadius(random)); var offset = SampleOffset(random); var mass = SampleMass(random); var surfaceRotation = -Vector2.UnitX; if (random != null) { surfaceRotation.X = (float)(random.NextDouble() - 0.5) * 2; surfaceRotation.Y = (float)(random.NextDouble() - 0.5) * 2; surfaceRotation.Normalize(); } var primaryTurbulenceRotation = Vector2.UnitX; if (random != null) { primaryTurbulenceRotation.X = (float)(random.NextDouble() - 0.5) * 2; primaryTurbulenceRotation.Y = (float)(random.NextDouble() - 0.5) * 2; primaryTurbulenceRotation.Normalize(); } var secondaryTurbulenceRotation = Vector2.UnitY; if (random != null) { secondaryTurbulenceRotation.X = (float)(random.NextDouble() - 0.5) * 2; secondaryTurbulenceRotation.Y = (float)(random.NextDouble() - 0.5) * 2; secondaryTurbulenceRotation.Normalize(); } var body = manager.AddBody(entity, offset + cellCenter); manager.AttachCircle(body, radius); var bounds = new FarRectangle(-radius, -radius, radius * 2, radius * 2); // Can be detected. manager.AddComponent <Indexable>(entity).Initialize(bounds, Detectable.IndexId); // Can make noise. manager.AddComponent <Indexable>(entity).Initialize(SoundSystem.IndexId); // Must be detectable by the camera. manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Remove when out of bounds or large containing cell dies. manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); manager.AddComponent <CellDeath>(entity).Initialize(false); // Make it attract stuff if it has mass. if (mass > 0) { manager.AddComponent <Gravitation>(entity).Initialize(Gravitation.GravitationTypes.Attractor); } // Damage stuff that touches a sun. manager.AddComponent <CollisionDamage>(entity).Initialize(false); var attributes = manager.AddComponent <Attributes <AttributeType> >(entity); attributes.SetBaseValue(AttributeType.AttackBurnMinDamage, 1000); attributes.SetBaseValue(AttributeType.AttackBurnMaxDamage, 2000); attributes.SetBaseValue(AttributeType.AttackBurnChance, 1); attributes.SetBaseValue(AttributeType.AttackBurnMinDuration, 5); attributes.SetBaseValue(AttributeType.AttackBurnMaxDuration, 5); attributes.SetBaseValue(AttributeType.Mass, mass); // Make it detectable. manager.AddComponent <Detectable>(entity).Initialize("Textures/Radar/Icons/radar_sun"); // Make it glow. manager.AddComponent <SunRenderer>(entity) .Initialize(UnitConversion.ToScreenUnits(radius) * 0.95f, surfaceRotation, primaryTurbulenceRotation, secondaryTurbulenceRotation, _tint); // Make it go whoooosh. manager.AddComponent <Sound>(entity).Initialize("Sun"); return(entity); }
/// <summary>Sets up the basic ship data that is deterministic.</summary> /// <param name="manager"> </param> /// <param name="faction"></param> /// <param name="position"></param> /// <returns></returns> private int CreateShip(IManager manager, Factions faction, FarPosition position) { var entity = manager.AddEntity(); var body = manager.AddBody(entity, type: Body.BodyType.Dynamic, worldPosition: position, allowSleep: false); // We need at least one fixture to carry on our index and collision groups. manager.AttachCircle( body, UnitConversion.ToSimulationUnits(_collisionRadius), collisionCategory: faction.ToCollisionGroup(), collisionMask: ~(faction.ToCollisionGroup() | Factions.Shields.ToCollisionGroup())); // These are 'worst-case' bounds, i.e. no ship should get larger than this. We use it as // a buffer for the camera and interpolation system, to allow them picking up on objects // that are positioned outside the viewport, but reach into it due to their size. var bounds = new FarRectangle(-2, -2, 4, 4); // Can be detected. manager.AddComponent <Indexable>(entity).Initialize(Detectable.IndexId); // Can be attracted. manager.AddComponent <Indexable>(entity).Initialize(GravitationSystem.IndexId); // Can make noise. manager.AddComponent <Indexable>(entity).Initialize(SoundSystem.IndexId); // Must be detectable by the camera. manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Rendering should be interpolated. manager.AddComponent <Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId); // Although 'unrealistic' in space, make ships stop automatically if not accelerating. body.LinearDamping = LinearDamping; manager.AddComponent <Gravitation>(entity).Initialize(); manager.AddComponent <ShipControl>(entity); manager.AddComponent <WeaponControl>(entity); manager.AddComponent <Energy>(entity); manager.AddComponent <Health>(entity).Initialize(120); manager.AddComponent <ShipDrawable>(entity) .Initialize(_texture, Color.Lerp(Color.White, faction.ToColor(), 0.5f)); manager.AddComponent <ParticleEffects>(entity); // Faction component, which allows checking which group the ship // belongs to. manager.AddComponent <Faction>(entity).Initialize(faction); // Make it detectable by AI and show up on the radar. manager.AddComponent <Detectable>(entity).Initialize("Textures/ship", true); // Controllers for maneuvering and shooting. manager.AddComponent <ShipInfo>(entity); // Create equipment slot. manager.AddComponent <SpaceItemSlot>(entity).Initialize(Fuselage.TypeId); // Give it an inventory as well. manager.AddComponent <Inventory>(entity).Initialize(10); // Add some character! manager.AddComponent <Attributes <AttributeType> >(entity); // Do we drop stuff? if (_itemPool != null) { manager.AddComponent <Drops>(entity).Initialize(_itemPool); } // The the sound component for the thruster sound. manager.AddComponent <Sound>(entity).Initialize("Thruster"); return(entity); }
/// <summary>Makes the AI roams the specified area.</summary> /// <param name="area">The area.</param> public void Roam(ref FarRectangle area) { ((RoamBehavior)_behaviors[BehaviorType.Roam]).Area = area; PushBehavior(BehaviorType.Roam); }