/// <summary>
 /// converts a cartesian form vector to a polar form angle
 /// </summary>
 /// <param name="direction">the direction vector being converted</param>
 /// <returns>the polar form angle</returns>
 public static float CartesianToPolar(Vector direction)
 {
     if (direction.X == 0 && direction.Y == 0) // has no direction, so return angle of 0
     {
         return 0;
     }
     else
     {
         return (float)Math.Atan2((double)direction.X, -(double)direction.Y);
     }
 }
 /// <summary>
 /// sets up and begins new round
 /// </summary>
 /// <param name="form">the form being used for the game</param>
 public static void StartNew(Form form)
 {
     LeftMovement1 = false;
     RightMovement1 = false;
     LeftMovement2 = false;
     RightMovement2 = false;
     LastPlayerHit = Player.Player1;
     LabelLevels.Text = "Levels Completed: " + LevelsCompleted.ToString();
     UpdateScores();
     Balls = new List<Ball>();
     Blocks = new List<Block>();
     FormSize = new Vector(form.Size.Width, form.Size.Height - 100);
     PictureBox paddle1 = new PictureBox();
     form.Controls.Add(paddle1);
     if (CurrentMode == GameMode.Coop || CurrentMode == GameMode.Versus) // 2 player
     {
         PictureBox paddle2 = new PictureBox();
         form.Controls.Add(paddle2);
         PaddleObject2 = new Paddle(paddle2, new Vector(FormSize.X * 0.75f, FormSize.Y));
         PaddleObject1 = new Paddle(paddle1, new Vector(FormSize.X * 0.25f, FormSize.Y));
     }
     else // 1 player
     {
         PaddleObject1 = new Paddle(paddle1, new Vector(FormSize.X * 0.5f, FormSize.Y));
         PaddleObject2 = null;
     }
     currentForm = form;
     BallsInPlay = 0;
     AddBall();
     int blockRows = ((CurrentMode == GameMode.Classic) ? DefaultBlockRows : BlockRows);
     int blockColumns = ((CurrentMode == GameMode.Classic) ? DefaultBlockColumns : BlockColumns);
     int blockWidth = (int)FormSize.X / blockColumns;
     int blockHeight = (int)FormSize.Y / 3 / blockRows;
     const int blockFrame = 4;
     for (int x = 0; x < blockColumns; x++)
     {
         for (int y = 0; y < blockRows; y++)
         {
             float rowPercentage = (float)y / (float)blockRows;
             PictureBox newPictureBox = new PictureBox();
             newPictureBox.BackColor = Color.FromArgb((int)Math.Round(255 * Math.Max(0, 1 - rowPercentage * 2), 0), (int)Math.Round(255 * (1 - 2 * Math.Abs(0.5f - rowPercentage)), 0), (int)Math.Round(255 * Math.Max(0, rowPercentage * 2 - 1), 0));
             currentForm.Controls.Add(newPictureBox);
             Vector newBlockPosition = new Vector(x * blockWidth + blockFrame * 0.5f, y * blockHeight + blockFrame * 0.5f);
             Vector newBlockSize = new Vector(blockWidth - blockFrame, blockHeight - blockFrame);
             Block newBlock = new Block(newPictureBox, newBlockPosition, newBlockSize);
             newBlock.Points = 50 + 50 * (blockRows - y);
         }
     }
     gameThread = new Thread(GameThread);
     gameThread.IsBackground = true;
     gameThread.Start();
     form.Disposed += delegate { gameThread.Abort(); };
     InPlay = true;
 }
 /// <summary>
 /// calculates whether or not 2 boxes intersect using the AABB bounding boxes method
 /// </summary>
 /// <param name="position1">the vector position of the first box</param>
 /// <param name="size1">the vector size of the first box</param>
 /// <param name="position2">the vector position of the second box</param>
 /// <param name="size2">the vector size of the second box</param>
 /// <returns>whether the boxes intersect of not</returns>
 public static bool Intersects(Vector position1, Vector size1, Vector position2, Vector size2)
 {
     return (position1.X + size1.X >= position2.X && position1.X <= position2.X + size2.X && position1.Y + size1.Y >= position2.Y && position1.Y <= position2.Y + size2.Y);
 }
        /// <summary>
        /// try to move the ball. Handles block, paddle and wall collisions
        /// </summary>
        /// <param name="newVelocity">the velocity the ball is trying to move with</param>
        /// <param name="timeElapsed">the time since the last move</param>
        private void Move(Vector newVelocity, float timeElapsed)
        {
            Vector positionChange = Vector.Mul(newVelocity, timeElapsed * 1000);
            Vector position = this.Position;
            Vector size = this.Size;
            Vector newPosition = Vector.Add(position, positionChange);

            List<Block> blocksHit = new List<Block>();
            List<int> collidedRays = new List<int>();

            bool reflectedX = false;
            bool reflectedY = false;

            foreach (Block block in Game.Blocks)
            {
                if (Box.Intersects(block.Position, block.Size, newPosition, size))
                {
                    //blockHit = true;

                    // IMPORTANT --
                    // Each Vector ray of ball in collideswith algorithms SHOULD only be collided with once.
                    // More than once can result in blocks being destroyed that shouldn't!!!

                    CollisionReturn collisionBlockReturn = CollidesWithBlock(block, position, positionChange, collidedRays);
                    if (collisionBlockReturn.Intersects) // hits a block
                    {
                        if ((collisionBlockReturn.Edge == 0 || collisionBlockReturn.Edge == 2) && !reflectedY) // Y
                        {
                            newVelocity.Y *= -1;
                            reflectedY = true;
                        }
                        if ((collisionBlockReturn.Edge == 1 || collisionBlockReturn.Edge == 3) && !reflectedX) // X
                        {
                            newVelocity.X *= -1;
                            reflectedX = true;
                        }
                        this.Velocity = newVelocity;
                        blocksHit.Add(block);
                        collidedRays.Add(collisionBlockReturn.Ray);
                    }
                }
            }
            foreach (Block block in blocksHit)
            {
                Game.DestroyBlock(block);
            }
            if (blocksHit.Count <= 0)
            {
                if (Box.Intersects(Game.PaddleObject1.Position, Game.PaddleObject1.Size, newPosition, size))
                {
                    CollisionReturn collisionPaddleReturn = CollidesWithPaddle(Game.PaddleObject1, position, positionChange);
                    //System.Console.WriteLine(collisionPaddleReturn.Intersects + ", " + collisionPaddleReturn.length + ", " + collisionPaddleReturn.Edge);
                    if (collisionPaddleReturn.Intersects) // hits paddle
                    {
                        Game.LastPlayerHit = Player.Player1;
                        // ID EDGE IS 0 THEN ADD SPIN + ANGLE
                        if (collisionPaddleReturn.Edge == 0 || collisionPaddleReturn.Edge == 2) // Y
                        {
                            newVelocity.Y *= -1;
                            float speed = this.Speed;
                            float currentAngle = Angles.CartesianToPolar(newVelocity);
                            float newAngle = (position.X + size.X * 0.5f - Game.PaddleObject1.Position.X - Game.PaddleObject1.Size.X * 0.5f) / 175 * 90 + Game.PaddleObject1.Speed * 30; // left - negative angle, right - positive angle
                            newAngle += Angles.Deg(currentAngle);
                            while (newAngle < 0)
                            {
                                newAngle += 360;
                            }
                            if (newAngle < 180)
                            {
                                newAngle = Math.Min(newAngle, 85);
                            }
                            else
                            {
                                newAngle = Math.Max(newAngle, 275);
                            }
                            Vector newDirection = Angles.PolarToCartesian(Angles.Rad(newAngle));
                            newVelocity = Vector.Mul(newDirection, speed);
                            //newVelocity = new Vector((float)Math.Round(newVelocity.X, 0), (float)Math.Round(newVelocity.Y, 0));
                        }
                        else // X - 1 || 3
                        {
                            // 2 different edges have raycast at same distance, therefore wrong edge can be chosen when ball hits very edge
                            newVelocity.X *= -1;
                        }
                        this.Velocity = newVelocity;
                        Move(newVelocity, timeElapsed);
                    }
                }
                else if (Game.PaddleObject2 != null && Box.Intersects(Game.PaddleObject2.Position, Game.PaddleObject2.Size, newPosition, size))
                {
                    CollisionReturn collisionPaddleReturn = CollidesWithPaddle(Game.PaddleObject2, position, positionChange);
                    //System.Console.WriteLine(collisionPaddleReturn.Intersects + ", " + collisionPaddleReturn.length + ", " + collisionPaddleReturn.Edge);
                    if (collisionPaddleReturn.Intersects) // hits paddle
                    {
                        Game.LastPlayerHit = Player.Player2;
                        // ID EDGE IS 0 THEN ADD SPIN + ANGLE
                        if (collisionPaddleReturn.Edge == 0 || collisionPaddleReturn.Edge == 2) // Y
                        {
                            newVelocity.Y *= -1;
                            float speed = this.Speed;
                            float currentAngle = Angles.CartesianToPolar(newVelocity);
                            float newAngle = (position.X + size.X * 0.5f - Game.PaddleObject2.Position.X - Game.PaddleObject2.Size.X * 0.5f) / 175 * 90 + Game.PaddleObject2.Speed * 30; // left - negative angle, right - positive angle
                            newAngle += Angles.Deg(currentAngle);
                            while (newAngle < 0)
                            {
                                newAngle += 360;
                            }
                            if (newAngle < 180)
                            {
                                newAngle = Math.Min(newAngle, 85);
                            }
                            else
                            {
                                newAngle = Math.Max(newAngle, 275);
                            }
                            Vector newDirection = Angles.PolarToCartesian(Angles.Rad(newAngle));
                            newVelocity = Vector.Mul(newDirection, speed);
                            //newVelocity = new Vector((float)Math.Round(newVelocity.X, 0), (float)Math.Round(newVelocity.Y, 0));
                        }
                        else // X - 1 || 3
                        {
                            // 2 different edges have raycast at same distance, therefore wrong edge can be chosen when ball hits very edge
                            newVelocity.X *= -1;
                        }
                        this.Velocity = newVelocity;
                        Move(newVelocity, timeElapsed);
                    }
                }
                else // check walls
                {
                    bool velocityChanged = false;
                    bool ballDestroyed = false;
                    if (newPosition.X < 0)
                    {
                        newVelocity.X *= -1;
                        velocityChanged = true;
                    }
                    if (newPosition.X + size.X > Game.FormSize.X)
                    {
                        newVelocity.X *= -1;
                        velocityChanged = true;
                    }
                    if (newPosition.Y < 0)
                    {
                        newVelocity.Y *= -1;
                        velocityChanged = true;
                    }
                    if (newPosition.Y + size.Y > Game.FormSize.Y)
                    {
                        Game.DestroyBall(this);
                        ballDestroyed = true;
                    }
                    if (!ballDestroyed)
                    {
                        if (velocityChanged)
                        {
                            this.Velocity = newVelocity;
                            Move(newVelocity, timeElapsed);
                        }
                        else
                        {
                            this.Position = newPosition;
                        }
                    }
                }
            }
            else
            {
                Move(newVelocity, timeElapsed);
            }
        }
        public int Points; // how many points the block awards when destroyed

        #endregion Fields

        #region Constructors

        // class constructor. A block will not move, so I can set the edges at creation, assuming they will never move
        public Block(PictureBox picture, Vector position, Vector size)
        {
            PictureBox = picture;
            Position = position;
            Size = size;
            Edges[TopEdge] = new Ray(position, new Vector(Size.X, 0)); // top edge
            Edges[RightEdge] = new Ray(Vector.Add(position, new Vector(Size.X, 0)), new Vector(0, Size.Y)); // right edge
            Edges[BottomEdge] = new Ray(Vector.Add(position, new Vector(0, Size.Y)), new Vector(Size.X, 0)); // bottom edge
            Edges[LeftEdge] = new Ray(position, new Vector(0, Size.Y)); // left edge
            Game.Blocks.Add(this);
        }
        /// <summary>
        /// calculates collisions between the ball and a block, and finds the block surface tha was hit
        /// </summary>
        /// <param name="block">the block being hit</param>
        /// <param name="newPosition">the position the ball has</param>
        /// <param name="newVelocity">the velocity the ball is trying to move with</param>
        /// <param name="collidedRays">a list of rayIDs which have already collided with a surface</param>
        /// <returns>the position, length and surface of the collision</returns>
        public CollisionReturn CollidesWithBlock(Block block, Vector newPosition, Vector newVelocity, List<int> collidedRays)
        {
            // vector rays of moving ball
            Ray[] Vectors = new Ray[4];
            Vectors[TopEdge] = new Ray(newPosition, newVelocity);
            Vectors[RightEdge] = new Ray(Vector.Add(newPosition, new Vector(Size.X, 0)), newVelocity);
            Vectors[BottomEdge] = new Ray(Vector.Add(newPosition, new Vector(0, Size.Y)), newVelocity);
            Vectors[LeftEdge] = new Ray(Vector.Add(newPosition, new Vector(Size.X, Size.Y)), newVelocity);

            CollisionReturn collisionReturn = new CollisionReturn(false);

            int rayID = 0;
            foreach (Ray ray in Vectors)
            {
                int edgeID = 0;
                if (!collidedRays.Contains(rayID)) // if the ray being used hasn't already collided with a box
                {
                    // check every edge of the block
                    foreach (Ray edge in block.Edges)
                    {
                        RaycastReturn raycastReturn = Ray.Raycast(ray, edge); // see if the ray meets the edge
                        if (raycastReturn.Intersects)
                        {
                            // if there has already been a collision, check for a shorter one
                            if (collisionReturn.Intersects)
                            {
                                if (raycastReturn.length < collisionReturn.length) // if the ball velocity ray hits this edge before any other ray hits an edge
                                {
                                    collisionReturn = new CollisionReturn(raycastReturn);
                                    collisionReturn.Edge = edgeID;
                                    collisionReturn.Ray = rayID;
                                }
                            }

                            // haven't had a ray collision yet, so use this collision by default
                            else
                            {
                                collisionReturn = new CollisionReturn(raycastReturn);
                                collisionReturn.Edge = edgeID;
                                collisionReturn.Ray = rayID;
                            }
                        }
                        edgeID++; // check next edge
                    }
                }
                rayID++; // check next ray
            }
            return collisionReturn; // return results
        }
        /// <summary>
        /// calculates collisions between the ball and a paddle, and finds the paddle surface that was hit
        /// </summary>
        /// <param name="paddle">the paddle that is being hit</param>
        /// <param name="newPosition">the position the ball has</param>
        /// <param name="newVelocity">the velocity the ball is trying to move with</param>
        /// <returns>the position, length and surface of the collision</returns>
        public CollisionReturn CollidesWithPaddle(Paddle paddle, Vector newPosition, Vector newVelocity)
        {
            Ray[] Vectors = new Ray[4]; // vector rays of moving ball
            Vectors[TopEdge] = new Ray(newPosition, newVelocity);
            Vectors[RightEdge] = new Ray(Vector.Add(newPosition, new Vector(Size.X, 0)), newVelocity);
            Vectors[BottomEdge] = new Ray(Vector.Add(newPosition, new Vector(0, Size.Y)), newVelocity);
            Vectors[LeftEdge] = new Ray(Vector.Add(newPosition, new Vector(Size.X, Size.Y)), newVelocity);

            CollisionReturn collisionReturn = new CollisionReturn(false);

            foreach (Ray ray in Vectors)
            {
                int edgeID = 0;
                foreach (Ray edge in paddle.Edges)
                {
                    RaycastReturn raycastReturn = Ray.Raycast(ray, edge);
                    if (raycastReturn.Intersects)
                    {
                        if (collisionReturn.Intersects)
                        {
                            if (raycastReturn.length < collisionReturn.length)
                            {
                                collisionReturn = new CollisionReturn(raycastReturn);
                                collisionReturn.Edge = edgeID;
                            }
                        }
                        else
                        {
                            collisionReturn = new CollisionReturn(raycastReturn);
                            collisionReturn.Edge = edgeID;
                        }
                    }
                    edgeID++;
                }
            }
            return collisionReturn;
        }
 /// <summary>
 /// to simplify adding 2 vectors together
 /// </summary>
 /// <param name="vector1">the first vector being added</param>
 /// <param name="vector2">the second vector being added</param>
 /// <returns>the composition of the 2 vectors</returns>
 public static Vector Add(Vector vector1, Vector vector2)
 {
     return new Vector(vector1.X + vector2.X, vector1.Y + vector2.Y);
 }
 /// <summary>
 /// to simplify multiplying a vector by a scale factor
 /// </summary>
 /// <param name="vector">the vector being multiplied</param>
 /// <param name="alpha">the scale factor to multiply it with</param>
 /// <returns>the product of the vector and the scale factor</returns>
 public static Vector Mul(Vector vector, float alpha)
 {
     return new Vector(vector.X * alpha, vector.Y * alpha);
 }
Esempio n. 10
0
        /// <summary>
        /// will check for collisions between 2 rays and return the results
        /// </summary>
        /// <param name="ray1">the first ray in the collision</param>
        /// <param name="ray2">the second ray in the collision</param>
        /// <returns>the position and length of the raycast collision</returns>
        public static RaycastReturn Raycast(Ray ray1, Ray ray2)
        {
            // shorten the variable names
            float x1 = ray1.Position.X;
            float y1 = ray1.Position.Y;
            float a1 = ray1.Direction.X;
            float b1 = ray1.Direction.Y;
            float x2 = ray2.Position.X;
            float y2 = ray2.Position.Y;
            float a2 = ray2.Direction.X;
            float b2 = ray2.Direction.Y;

            if (a1 * b2 == a2 * b1) // parallel, so won't collide
            {
                return new RaycastReturn(false); // rays don't collide, so return false collision
            }
            else // they collide at some point, given their lengths are infinite
            {
                float i = (x2 * b2 + a2 * y1 - a2 * y2 - x1 * b2) / (a1 * b2 - a2 * b1); // distance between start and collision along ray1
                Vector position = new Vector(x1 + i * a1, y1 + i * b1); // form coordinates of collision
                if (i > 1 || i < 0) // outside length of ray1
                {
                    return new RaycastReturn(false); // do not meet each other, so return false collision
                }
                else // they collide within their lengths
                {
                    float length = i * ray1.Length; // length of ray1 up to collision
                    return new RaycastReturn(true, position, length); // return true collision
                }
            }
        }
Esempio n. 11
0
 // detailed class constructor for (true) raycast collisions
 public RaycastReturn(bool intersects, Vector position, float length)
 {
     this.Intersects = intersects;
     this.Position = position;
     this.length = length;
 }
Esempio n. 12
0
        public Vector Velocity; // the speed and direction of the ball in vector form

        #endregion Fields

        #region Constructors

        // class constructor, requires a picturebox and a starting velocity
        public Ball(PictureBox picture, Vector velocity)
        {
            this.PictureBox = picture;
            this.Velocity = velocity;

            // the game is in a 2 player mode, and player 2 lost the ball, so player 2 gets the new ball
            if ((Game.CurrentMode == GameMode.Coop || Game.CurrentMode == GameMode.Versus) && Game.LastPlayerLost == Player.Player2)
            {
                this.Position = new Vector(Game.PaddleObject2.Position.X + Game.PaddleObject2.Size.X * 0.5f - Size.X * 0.5f, Game.PaddleObject2.Position.Y - Size.Y);
                Game.LastPlayerHit = Player.Player2; // player 2 starts with the ball
            }

            // otherwise player1 gets the ball
            else
            {
                this.Position = new Vector(Game.PaddleObject1.Position.X + Game.PaddleObject1.Size.X * 0.5f - Size.X * 0.5f, Game.PaddleObject1.Position.Y - Size.Y);//new Vector(Game.FormSize.X * 0.5f - Size.X * 0.5f, Game.FormSize.Y * 0.5f - Size.Y * 0.5f);
                Game.LastPlayerHit = Player.Player1; // player 1 starts with the new ball
            }
            Game.Balls.Add(this);
        }
Esempio n. 13
0
 // class constructor for length if unit direction vector is given
 public Ray(Vector position, Vector direction, float length)
 {
     this.Position = position;
     this.Direction = direction;
     this.Length = length;
 }
Esempio n. 14
0
 // class constructor with position and direction
 public Ray(Vector position, Vector direction)
 {
     this.Position = position;
     this.Direction = direction;
 }
Esempio n. 15
0
        public Vector Position; // form coordinates of the ray

        #endregion Fields

        #region Constructors

        // empty ray class constructor
        public Ray()
        {
            this.Position = new Vector();
            this.Direction = new Vector();
        }
Esempio n. 16
0
        private Ray[] edges = new Ray[4]; // privately stored box edges to be used internally, as it should only be set by CalculateEdges()

        #endregion Fields

        #region Constructors

        // class constructor, needs a picturebox and a starting position. The edges are not calculated as they are not needed at the start, and can still be used later
        public Paddle(PictureBox pictureBox, Vector position)
        {
            PictureBox = pictureBox;
            PictureBox.BackColor = Color.DodgerBlue;
            PictureBox.Size = new Size(150, 25);
            Position = new Vector(position.X - Size.X * 0.5f, position.Y - Size.Y);
        }