Skip to content

Polclard/Pong-Project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pong

Филип Насковски, Ален Јангелов
Апликацијата што ја развивме е класичната игра Понг претставена во 1972 од Atari. Секој играч контролира лопатка и се обидува да го погоди топчето покрај лопатката на противникот. Целта е да се освојат поени така што топката ќе помине покрај лопатката на противникот. Играта продолжува додека еден играч не достигне одреден праг на бодови, а потоа тие се прогласени за победници. Играта има за цел да го пресоздаде носталгичното искуство на оригиналната игра понг, нудејќи два режими на игра: играч против играч и играч против компјутер.

Упатство за користење

По стартување на апликацијата се вчитува почетното мени (слика 1). На почетното мени имаме 4 копчиња:
  • Player vs Player – ја започнува играта во режим на 2 играчи
  • Player vs Computer – ја започнува играта против компјутер, во еден од избраните режими “Easy”, “Medium”, “Hard”
  • Options – го отвора менито за опции
  • Exit – ја затвора апликацијата

Дизајн на почетно мени

Слика 1. Почетно мени

Player vs Player

Со одбирање на „Player vs Player“ се отвора прозорецот за играње. Пред да започне играта на екранот се претставени контролите. Играчот 1 ја контролира левата лопатка. Со кликање на копчето W, лопатката се поместува нагоре, а со кликање на S надолу. Играчот 2 ја контролира левата лопатка со стрелките за нагоре и надолу, соодветно. Играта се започнува со кликање на Space копчето, и со истото копче може да се паузира во било кој момент. Со кликање на Esc се затвора прозорецот. Со кликање и влечење на било кој дел од прозорецот, тој може да се поместува по десктопот.

Контроли на почеток

Слика 2. Поле за играње - Контроли

Поле за играње

Слика 3. Поле за играње

Играчот треба да освои 11 поени за да победи, а по победата се појавува MessageBox кој му честита на победникот, и од каде може да се започне реванш.

Прозорец кој се појавува по победа

Слика 4. Прозорец кој се појавува по победа

Player vs Computer

Со одбирање „Player vs Computer“ се отвора мало мени каде се понудени 3 нивоа на тежини: „Easy“, „Medium“, и „Hard“. Менито може да се види на сликата бр 5.

Мени за избор на тежина

Слика 5. Мени за избор на тежина

По одбирање на тежината ни се отвора истиот прозорец како кога избравме „Player vs Player“, но сега играчот може да ја контролира само левата лопатка, и се прикажани контролите само за левата лопатка.

Поле за играње против компјутер

Слика 6. Поле за играње против компјутер

Оptions

Доколку избереме „Options“ ни се појавува мени, каде може да селектираме дали сакаме Powerups во играта. Менито може да се види на слика 7.

Опции

Слика 7. Опции

Powerups

Доколку се вклучат, се појавуваат по одреден период некаде на полето на играње и можат да бидат добри или штетни за играчот кој ги погодил. Постојат 4 различни Powerups:
  • EnlargePaddle – ја зголемува должината на лопатката на играчот кој го погодил овој Powerup.
  • ShrinkPaddle – ја намалува должината на лопатката на играчот кој го погодил.
  • EnlargeOpposition – ја зголемува должината на лопатката на противникот.
  • ShrinkOpposition – ја намалува должината на лопатката на противникот.

Структура на апликацијата

Како што може да се види во упатството за користење, играта содржи 4 форми: Почетно мени, Прозорец за играње, опции, и избор на тежина. Целото дејство на „играње“ на играта се одвива во формата PlayingField.cs (Прозорец за играње).

PlayingField

Класата PlayingField ја претставува главната форма на играта. Класата е одговорна за управување со сцената на играта (класа Scene), играчите (класа Player), топката (Класа Ball) и интеракциите со корисниците. Оваа класа служи како влезна точка за играта и се справува со различни настани, како што се цртање на сцената, ажурирање на логиката на играта, управување со кориснички влезови и управување со Powerups. Шаблонот за дизајн што се користи во класата PlayingField е Model-View-Controller (MVC). Класата ги прима корисничките влезови и ги проследува до сцената. Во оваа класа е имплементиран дел од логиката на тајмерот. Со секој tick на тајмерот се повикува движење на топчето, движење на играчите и проверка на состојбата на топчето (дали има постигнато погодок, или е е погоден некој Powerup). Оваа класа е задолжена за следење на поените на секој играч и прогласување на победник, како и за паузирање и управување со powerups.

Scene

Класата Scene е задолжена за координирањето на компонентите и логиката на играта. Дејствува како централна точка, управувајќи со интеракциите помеѓу играчите, топката и Powerups. Класата обезбедува методи за додавање играчи на сцената, цртање на објектите на сцената, поместување на топчето според дејствијата на играчот, проверка на статусот на топчето (поен или не), проверка на статусот на Powerup, започнување нова рунда откако играчот ќе постигне поен и создавање на случаен Powerup.

Player

Со класата Player се претставени лопатките со кои играчите го удираат топчето. Содржи методи за движење нагоре и надолу, и Движењето на компјутерот. Player може да биде еден од 3 типа: Player1, Player2, AI. • Player1 е секогаш левата лопатка, контролирана од играчот со W и S. • Player2 e десната лопатка достапна во режимот „Player vs Player“, контролирана од вториот играч со стрелките од тастатурата. • AI е левата лопатка контролирана од компјутерот, достапна во „Player vs Computer“. Може да има 3 тежини “Easy”, “Medium”, и “Hard” зависно од изборот на играчот. Согласно избраната тежина движењето на лопатката е различно, нешто како Strategy шаблонот на дизајн. Повеќе за движењето може да се види подолу во делот „Поважни Алгоритми“.

Ball

Класата Ball го претставува топчето со кое играат играчите. Секогаш постои само едно топче на сцената. Ги опфаќа својствата и однесувањето на топчето, како местоположба, радиус, боја, и брзина на движење. Класата служи за цртање на топчето и проверка на судири со ѕид, играчи, и powerups. Класата ја следи моменталната положба на топчето, и доколку детектира судир со ѕид или играч ја менува насоката соодветно, а при судир со powerup или постигнување на погодок ја проследува информацијата до класата Scene.

Powerup

Класата Powerup претставува powerup објект што може да го соберат играчите. Ги содржи својствата и однесувањето, како што се неговиот тип, статус на активирање, позиција и визуелен изглед. Класата Powerup има својство Power кое го дефинира типот, постојат 4 типа: „EnlargePaddle“, ShrinkPaddle“, „EnlargeOpposition“, и ShrinkOpposition“. Active покажува дали powerup-от е моментално активен или не. Координатите се определуваат при креирање на рандом позиција, а бојата се одредува врз основа на неговиот тип. Кога играчот ќе собере напојување, се повикува методот Активирај. Го поставува својството Active на true и ја менува висината на лопатката на играчот врз основа на типот. На пример, „EnlargePaddle“, ја зголемува висината на лопатката на играчот. Спротивно на тоа, „ShrinkPaddle“ ја намалува висината на Методот Деактивирај се користи за ресетирање на ефектот, со поставување на својството Active на false и враќање на оригиналните висини на лопатките на двата играчи. Класата Powerup служи за да додаде нешто повозбудливо во играта.

Поважни Алгоритми

За синхронизација на сите елементи поставен е тајмер во формата PlayingField, со отчукување на секоја милисекунда.

Движење на топче

Движење на топче

Движењето на топчето на претставено со 2 вектори SpeedX и SpeedY. При креирање на топчето и двата вектори имаат вредност 5. Доколку е првата рунда векторот SpeedX се множи со случајна вредност (1 или -1) за да добие случајна насока лево или десно. При креирање на нова рунда, новокреираното топче добива како аргумент дали треба да оди лево или десно, и согласно тоа SpeedX се множи со соодветната вредност (-1 за лево, 1 за десно). При судир со горниот и долниот ѕид, SpeedY се множи со -1, што ја променува неговата вертикална насока.
Кодот со кој е опишано движењето може да се види подолу. За одбивање од лопатките се користи посложен алгоритам.

Одбивање на топче од лопатка

Одбивање на топче]

За поинтересно играње, топчето се одбива различно зависно од делот на лопатката од кој се одбива. За разлика од оригиналната игра во која топчето може да има само 8 насоки, во оваа верзија нема такво ограничување. Пресметување на агол на одбивање Како што е претходно споменато движењето на топчето е опишано со двете променливи SpeedX и SpeedY. Со помош на функцијата CheckCollisionWithPlayer дознаваме дали некој играч се има судрено со топчето. Функцијата враќа 1 за левата лопатка, 2 за десната, а 0 доколку нема судир. Функцијата го пресметува тоа соодветно координатите на секој играч, и координатите на топчето функцијата може да се види подолу.

public int CheckCollisionWithPlayer(Player Player_1, Player Player_2)
    {
        if((this.CenterCoordinates.X-Radius < Player_1.Coordinates.X + Player.Width) 
            && ((this.CenterCoordinates.Y + Radius > Player_1.Coordinates.Y) 
                && (this.CenterCoordinates.Y - Radius < Player_1.Coordinates.Y + Player_1.Height)))
        {
            return 1;
        }
        if ((this.CenterCoordinates.X+Radius > Player_2.Coordinates.X)
            && ((this.CenterCoordinates.Y + Radius > Player_2.Coordinates.Y) 
                && (this.CenterCoordinates.Y - Radius < Player_2.Coordinates.Y + Player_1.Height)))
        {
            return 2;
        }
        return 0;
    }

Откако ќе добиеме дали некој играч е во судир со топчето ја собираме вкупната брзина т.е. апсолутните вредностите на SpeedX и SpeedY во нова променлива TotalSpeed. Оваа променлива ја инкрементираме, што дава ефект на забрзување на топчето со секој удар. Потоа ја пресметуваме релативната положба на топчето во однос на центарот на лопатката со која се има судрено. Со променливата relativeY ја добиваме вредноста на X координатата во однос на лопатката а со функцијата Math.Abs(relativeY - (PlayerHeight/2)) ја добиваме оддалеченоста од центарот. Оддалеченоста од центарот ја делиме со половина од должината на лопатката, што ни ја дава процентуалната оддалеченост од центарот, која тежи кон нула кога е поблиску кон центарот. Оваа вредност ја користиме како тежински фактор за SpeedY. За да не добиеме целосно хоризонтално движење на топчето, што ќе ја заглави играта во место оваа вредност ја множиме со 0.7. Вредноста на SpeedY ја добиваме множејќи ја вкупната брзина со новодобиената тежина. Вредноста на SpeedX ја добиваме одземајќи ја SpeedY од вкупната брзина. На крајот ја корегираме насоката доколку играчот 2 го има удрено топчето го SpeedX го множиме со -1, и доколку топчето е удрено со горната половина на лопатката SpeedY се множи по -1. Целиот код може да се види подолу.

Код за движење и удари

public void Move(Player Player_1, Player Player_2, int Form_Width, int Form_Height)
    {
        CenterCoordinates = new Point(CenterCoordinates.X + SpeedX, CenterCoordinates.Y + SpeedY);
        CheckCollision(Player_1, Player_2, Form_Width, Form_Height);
    }

public void CheckCollision(Player Player_1, Player Player_2, int Form_Width, int Form_Height)
    {
        //collision with top wall
        if (this.CenterCoordinates.Y <= 0 && SpeedY < 0)
        {
            SpeedY *= -1;
        }

        //collision with bottom wall
        if (this.CenterCoordinates.Y > Form_Height - Radius && SpeedY > 0)
        {
            SpeedY *= -1;
        }

        //collision with player paddles
        int PlayerCollided = CheckCollisionWithPlayer(Player_1, Player_2);
        if (PlayerCollided > 0)
        {
            //change speedY depending on area of paddle hit
            int TotalSpeed = Math.Abs(SpeedX) + Math.Abs(SpeedY);
            TotalSpeed++;

            //get what player was in contact with the ball
            int PlayerY = Player_1.Coordinates.Y;
            if(PlayerCollided == 2) PlayerY = Player_2.Coordinates.Y;

            //get position of ball relative to the hit player
            int relativeY = this.CenterCoordinates.Y - PlayerY;
            
            //calculate the speed vectors 
            double Yweight = Math.Abs(relativeY - 60) / 60.0;
            SpeedY = (int) (TotalSpeed * Yweight * 0.7);
            SpeedX = TotalSpeed - SpeedY;

            
            //correct direction
            if(PlayerCollided == 2 ) SpeedX *= -1;
            
            if (relativeY < 60) SpeedY *= -1;

           
        }
                  
    }

AIMovement

Алгоритмот за играње од компјутерот функционира на многу едноставен начин. Ја следи вредноста на Y координатата на топчето, во однос на центарот на лопатката. Доколку топчето се наоѓа над центарот на лопатката, лопатката оди нагоре, во спротивно оди надолу. Слично како Strategy шаблонот, овој алгоритам се приспособува зависно од одбраната вредност за тежина на играта. Доколку играчот има избрано „Easy“ режим, се воведува XThreshold, кој ја претставува границата од која лопатката почнува да го следи топчето. Оваа граница е поставена на 2/3 од ширината на полето, т.е. Лопатката го следи топчето само во последната третина, што ја забавува реакцијата на компјутерот и ја зголемува можноста за добивање поен. Во „Medium“ режимот XThreshold има вредност 0, и го следи топчето за целото време додека се движи кон десната страна, а во „Hard“ режимот доколку топчето се движи кон играчот а не кон компјутерот (кон левата страна) лопатката се враќа во средина, што овозможува да биде поблиску до точката каде би удрило топчето. Ова го намалува растојанието до точката каде топчето ќе удри, со што го намалува времето потребно на лопатката да стигне до таа точка. Кодот е достапен подолу.
    public void AIMovement(Ball MovingBall, int FormWidth, int FormHeight)
    {
        if(Name != "Player1" && Name !="Player2")
        {
            int XThreshold = 0;

            if(Name == "Easy")  XThreshold = FormWidth - (FormWidth / 3);
            
            if(MovingBall.SpeedX > 0 && MovingBall.CenterCoordinates.X > XThreshold)
            { 
                Point PointToHit = new Point(Coordinates.X, Coordinates.Y + Height / 2);
                int Y = MovingBall.CenterCoordinates.Y;
                if (PointToHit.Y > Y && Math.Abs(PointToHit.Y - Y) > 10)
                {
                    MoveUp();
                }
                else if (PointToHit.Y < Y && Math.Abs(PointToHit.Y - Y) > 10)
                {
                    MoveDown();
                }
            }

            if(Name == "Hard" && MovingBall.SpeedX < 0)
            {
                Point PointToHit = new Point(Coordinates.X, Coordinates.Y + Height / 2);
                int Y = FormHeight/2;
                if (PointToHit.Y > Y)
                {
                    MoveUp();
                }
                else if (PointToHit.Y < Y)
                {
                    MoveDown();
                }
            }
        }
    }

About

Ball-Bounce-Project Repo

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages