/// <summary> /// Initializes a new <see cref="UfoController"/>. /// </summary> public UfoController() { JsonObject jsonObject = MainGame.Context.Content.Load <JsonObject>("UfoScores"); scores = JsonConvert.DeserializeObject <int[]>(jsonObject.JsonSource); spaceInvadersFont = MainGame.Context.Content.Load <SpriteFont>("SpaceInvadersFont"); timeToSpawn = Random.Range(MinimumSpawnTime, MaximumSpawnTime); ufoTexture = MainGame.Context.MainTextureAtlas["ufo"]; BoundingRectangle = new RectangleF(0, 0, ufoTexture.Width * MainGame.ResolutionScale, ufoTexture.Height * MainGame.ResolutionScale); flashScore = false; }
/// <summary> /// Update the <see cref="UfoController"/>. /// </summary> /// <param name="deltaTime"></param> public void Update(float deltaTime) { if (MainGame.Context.GetGameScreen <GameplayScreen>(GameScreenType.Gameplay).IsFrozen) { return; } float ufoWidth = ufoTexture.Width * MainGame.ResolutionScale; if (flashScore) { UpdateScoreFlash(deltaTime); return; } if (isUfoActive) { BoundingRectangle.X += HorizontalSpeed * movementDirection * deltaTime; // Check if the UFO has gone out of bounds if (movementDirection == 1 && BoundingRectangle.X - ufoWidth * MainGame.ResolutionScale > MainGame.GameScreenWidth || movementDirection == -1 && BoundingRectangle.X < -ufoWidth) { Destroy(false); } } else { timeToSpawn -= deltaTime; if (!(timeToSpawn <= 0)) { return; } // A 50% chance to spawn on the left side. bool left = Random.Value() > 0.5f; movementDirection = left ? 1 : -1; isUfoActive = true; float positionY = GameplayScreen.TopVerticalBoundary + ufoTexture.Height * MainGame.ResolutionScale + VerticalSpawnPadding; float positionX = left ? -ufoWidth : MainGame.GameScreenWidth + ufoWidth; BoundingRectangle.Position = new Vector2(positionX, positionY); timeToSpawn = Random.Range(MinimumSpawnTime, MaximumSpawnTime); } }
/// <summary> /// <para> /// Gets the time until an <see cref="Enemy"/> can attack, in seconds, implemented as a range /// between a pair of exponential functions. /// </para> /// <remarks> /// More formally, the upper and lower bounds of the enemy attack times are given by a pair of functions /// f(x) and g(x) where f(x) represents the maximum time, g(x) represents the minimum time, and x represents the /// change in the vertical distance, in pixels (x in [0, d] where d represents the max vertical distance). /// The enemy attack time, t(x), is defined as a random value uniformly distributed on [f(x), g(x)]: t(x) ~ U([f(x), g(x)]) /// where U represents the distribution function. For the full equations, see <see href="https://www.desmos.com/calculator/wzpbqmwmwt"></see>. /// </remarks> /// </summary> /// <returns></returns> private float GetEnemyAttackTime() { // The initial maximum time until an enemy attacks, in seconds; the value of g(0). const float initialMaximumTime = 30; // The initial minimum time until an enemy attacks, in seconds; the value of f(0). const float initialMinimumTime = 2; // The final maximum time until an enemy attacks, in seconds; the value of g(d). const float finalMaximumTime = 6; // The final minimum time until an enemy attacks, in seconds; the value of f(d). const float finalMinimumTime = 1; float validVerticalDistance = 1 / (MainGame.Context.GetGameScreen <GameplayScreen>(GameScreenType.Gameplay).BarrierGroup[0].Rectangle.Top - StartingPosition.Y); float b1 = (float)Math.Pow(initialMinimumTime / initialMaximumTime, validVerticalDistance); float b2 = (float)Math.Pow(finalMinimumTime / finalMaximumTime, validVerticalDistance); float distance = boundingRectangle.Y - StartingPosition.Y; float maximumTime = initialMaximumTime * (float)Math.Pow(b1, distance); float minimumTime = finalMaximumTime * (float)Math.Pow(b2, distance); return(Random.Range(minimumTime, maximumTime)); }
/// <summary> /// Retrieves a random projectile prototype with the specified type. /// </summary> /// <param name="type">The type of the projectile prototype.</param> private Projectile GetRandomProjectilePrototype(ProjectileType type) => projectilePrototypes[type][Random.Range(projectilePrototypes[type].Count)];