zoukankan      html  css  js  c++  java
  • 小游戏个人

      这次我主要负责写英雄类,伤害类和主函数。

      之前用过用过游戏引擎写过几个小游戏,所以一开始大概有点思路。

    主函数

    ALL_ini();//初始化游戏
    while
    (1)//死循环 { GetLocalTime(&now);//开始执行代码的时间 //游戏此处执行 ALL_draw();//绘制窗口 ALL_play();//游戏运行 GetLocalTime(&last); //执行完代码的时间 sleep_time= find_time(last, now); Sleep(max(49 - sleep_time,0));//休息一段时间 GetLocalTime(&last); //执行完代码的时间
          if ( win())   {   break;   } }

        主函数,游戏进行时的死循环代码。几段和时间有关的代码是用来控制帧率,也就是游戏1s中执行多少次的。每次运行完一次游戏,计算一下运行需要多久,然后通过Sleep函数控制休息时间,已控制每次循环开始的时间相差是固定的。

        最后一个是判断双方基地炸了没的代码。

    图片的读取和显示

        这个大概是这次任务碰到的最大的两个问题之一,主要是因为不知道去哪里找相关的代码,然后大部分的例子都有用到窗口类之类的东西,所以看半天也看不懂是啥意思。

        最开始找到的显示图片的是用IMAGE这个类,但是PNG图片不能用,只能显示jpg的图片。因为受不了jpg的白底,所以后面又找到了CImage这个类。然后我就被CImage里hdc这个参数卡了很久很久。后面突然想通

    hwnd = initgraph(640, 480);//创建窗口
    hdc = GetDC(hwnd);//获得窗口hdc
    bac.Draw(hdc, 0, 0);

       先获得hwnd,然后获得hdc,然后再在窗口上画就可以了。虽然到现在还是没搞懂这些代码是啥意思。但是大致知道怎么用了。

    闪屏问题的解决

      这个问题其实到游戏几乎制作完成以后我才去解决,因为之前绘制的图片比较少,并没有出现闪屏问题,但是虽然绘制的图片增加,闪屏越来越严重。

      然后去百度,但是都是说什么双缓冲,一些完全听不懂的名词,完全没看懂。不过无意间发现CImage这个类有个一个函数GetDC(),然后猜,既然这个图片也有DC,那肯定也可以在这个图片上绘制其他图片。

      所以我就先获得一张空白图片的DC,然后把其他所有图片都覆盖上去,在把这张绘制了全部图片的图片绘制到窗口上,这样每次游戏运行就只绘制了一张图片,闪屏的问题就解决了。PS:不过所以不透明度低于255的图片都会变得很奇怪,所以我就把几乎所有

    图片透明度都调到了255。不过技能CD中的动画就变得很难看了,要啥自行车。

    游戏运行的核心

       所有的物体,都有这样一个函数。按照游戏引擎的叫法,这个叫同步事件。

    void step( list<tower> &Solid, list<Army> &Army_Slime, list<hurt> &Magic,queue<long long > &dead, hero &player_red,hero &player_blue,long long &object_ID)

      每个执行一个上面那个死循环么,所有物体都同步时间都会得到一次执行。

      其中的几个参数分别是,防御塔的链表(包括基地和泉水),小兵的列表,所有伤害(子弹,英雄的技能等等)的列表,死亡物体的ID队列。和两个英雄以及所以物体的ID。游戏运行时,就是把三个列表里的物体和两个英雄的同步时间执行一下(本来英雄也想写成列表的,就俩个觉得没必要就没写,但是导致代码量增加了一点)。object_ID也是这个游戏中一个重要的变量,每次创建一个物体,都会获得当前的object_ID的值,然后再object_ID++,以确保任何物体的ID不会重复,object_ID的其中一个作用就是清除物体。而清除物体还需要上面的第三个参数dead队列。在物体的运行过程中,出现需要死亡的情况(比如小兵没血了)又不好立即把自己移除出自己所在的列表时,会把自己的ID加入到dead这个队列。在所有物体的同步时间执行结束以后,会清除ID存在于dead队列的物体。当一个物体要

    检测碰撞也需要用到那几个列表,比如当小兵判断是否会受到伤害时,需要判断伤害这个列表中所有敌方子弹是否会碰撞到自己。

      游戏运行的处的代码;

    void ALL_play()//游戏在此运行 //已经写完
    {
        for (list<hurt>::iterator i = Magic.begin(); i != Magic.end(); ++i)//特效同步
            i->step(Solid, Army_Slime, Magic, dead, player_red, player_blue, object_ID);
        player_red.step(Solid, Army_Slime, Magic, dead, player_red, player_blue, object_ID);
        player_blue.step(Solid, Army_Slime, Magic, dead, player_red, player_blue, object_ID);
    
        for (list<tower>::iterator i = Solid.begin(); i != Solid.end(); ++i)//防御塔同步
            i->step(Solid, Army_Slime, Magic, dead, player_red, player_blue,object_ID);
        for (list<Army>::iterator i = Army_Slime.begin(); i != Army_Slime.end(); ++i)//小兵同步
            i->step(Solid, Army_Slime, Magic, dead, player_red, player_blue, object_ID);    
        while (dead.empty() == false)//清除已死的东东。
        {
            long long temp = dead.front();
            dead.pop();
            for (list<tower>::iterator i = Solid.begin(); i != Solid.end(); )
            {
                if (i->ID == temp)
                    i = Solid.erase(i);
                else
                    ++i;
            }//删除防御塔
            for (list<Army>::iterator i = Army_Slime.begin(); i != Army_Slime.end(); )//防御塔同步
            {
                if (i->ID == temp)
                    i = Army_Slime.erase(i);
                else
                    ++i;
            }//删除小兵
            for (list<hurt>::iterator i = Magic.begin(); i != Magic.end(); )//防御塔同步
            {
                if (i->ID == temp)
                    i = Magic.erase(i);
                else
                    ++i;
            }//删除特效
        }        
        count_time++;//计算运行次数  这句话卵用没有,我也不知道写这个干啥
    }

      绘制函数,在每帧结束重绘整个窗口,比较简单不多废话

    void ALL_draw(/*绘制函数*/)
    {
        hdc2 = bac.GetDC();
        bac_ini.Draw(hdc2, 0, 0);
    
        Draw_num(1280, 64, player_red.LV, hdc2);
        Draw_num(1280+128, 64, player_red.EXE, hdc2);
        Draw_num(1280 + 128*2, 64, player_red.weak_time, hdc2);
    
        Draw_num(96, 672, player_blue.LV, hdc2);
        Draw_num(96+128, 672, player_blue.EXE, hdc2);
        Draw_num(96 + 128*2, 672, player_blue.weak_time, hdc2);
        
        wall.Draw(hdc2, 0, 192);//城墙
        wall.Draw(hdc2, 0, 544);//城墙 背景层次最低
    
    
        for (list<tower>::iterator i = Solid.begin(); i != Solid.end(); ++i)
        {
            if ( i->who==0 )
                i->draw_myself(hdc2, temp_pic, strip_hp); //此处绘制绘制泉水
        }
    
        player_red.draw_myself(hdc2, temp_pic, strip_hp);//绘制英雄
        player_blue.draw_myself(hdc2, temp_pic,strip_hp);//
        
        for (list<Army>::iterator i = Army_Slime.begin(); i != Army_Slime.end(); ++i)
            i->draw_myself(hdc2, temp_pic, strip_hp); //此处小兵
    
        for (list<tower>::iterator i = Solid.begin(); i != Solid.end(); ++i)
        {
            if (i->who != 0)
                i->draw_myself(hdc2, temp_pic, strip_hp); //此处绘制绘制水晶和防御塔
        }
    
        for (list<hurt>::iterator i = Magic.begin(); i != Magic.end(); ++i)
            i->draw_myself(hdc2, temp_pic, strip_hp); //此处绘制伤害

    Draw_num(120, 32, player_red.hp, hdc2); Draw_num(120, 96, player_red.mp, hdc2); Draw_num(1600, 640, player_blue.hp, hdc2); Draw_num(1600, 640+64, player_blue.mp, hdc2); bac.Draw(hdc, 0, 0); }

     英雄类

        写这个类的时候也吃了个没知识的亏,试过各种控制英雄的方法,后面用getch成功了,一开始单单控制一个英雄的时候什么问题都没有,但是当我引入其他物体的时候就发现,getch会和getchar一样,运行到这一处时会停止,直到某个按键按下。

        ·后面找了很久终于某篇博客中找到了这么一句话

    #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)

        和前面找的东西一样,我看不懂这是什么意思。不过知道了加了这句以后,就相当于多了一个KEY_DOWN(x)的函数,x是一个字符,作用是判断当前情况下x字符对应的按键是否按下。英雄的控制问题就迎刃而解了。

        英雄类的同步事件。一开始写的时候觉得可能很复杂,不过把技能当成一个类分出去就简单很多了

        同步时间里,除了每个类都有的碰撞检测外,英雄类还有一些升级和控制的碰到。四方向移动的控制很简单,只需要判断要前往的位置有没有固体就可以了,而技能的释放在技能作为一个类分离以后也简单了很多。直接用一个for语句就写完了。

    void hero::step(list<tower> &Solid, list<Army> &Army_Slime, list<hurt> &Magic, queue<long long > &dead, hero &player_red, hero &player_blue,long long &object_ID) {//同步事件需要传入所有链表
        move = 0;
        max_hp = ini_max_hp + 600 * (LV - 1);
        max_mp = ini_max_mp + 300 * (LV - 1);//最大MP增长
        //升级事件
        if ( EXE>=20 )
            LV = 2;
        if (EXE >= 50)
            LV = 3;
        if (EXE >= 90)
            LV = 4;
        if (EXE >= 140)
            LV = 5;
        //死亡事件 被催眠
        //碰到水晶持续回血 碰到敌方攻击收到伤害
        for (list<tower>::iterator i = Solid.begin(); i != Solid.end(); ++i)//防御塔
        {
            if ( i->who==0 && i->color==color )//是我方泉水
                if ( place_meet(x + wide, y + hight, i->x + i->wide, i->y + i->hight, R + i->R))
                {
                    hp += max_hp / 100;
                    mp += max_mp / 100;
                }
        }
        if (wudi == 0)//无敌效果
        {
            for (list<hurt>::iterator i = Magic.begin(); i != Magic.end(); ++i)//伤害
            {
                if ( i->who==1 )//追踪形技能切目标不是我
                    if ( i->aim_ID!=ID )
                        continue;
                if (color != i->color)//敌人的法术
                    if (place_meet(x + wide, y + hight, i->x + i->wide, i->y + i->hight, R + i->R))//被攻击到
                    {
                        hp -= i->atk;
                        if (i->who == 3)//百万吨拳击附带伤害
                            hp -= (max_hp - hp)/100;
                        if (i->who == 2 && angry==0)//催眠粉 狂暴时无法被催眠
                        {
                            sleep = 1;
                            weak_time = 20;
                        }
                    }
            }
        }
        else 
        {
            wudi_time--;
            if (wudi_time<=0)
            {
                wudi = 0;
            }
        }
        if (fast == 1)
        {
            fast_time--;
            if (fast_time <= 0)
                fast = 0;
            speed = ini_speed*2;
        }
        else
            speed = ini_speed ;
        if (angry == 1)//狂暴
        {
            atk = ini_atk[LV] * 2;
            angry_time--;
            if ( angry_time <=0 )
                angry = 0;
        }
        else
            atk = ini_atk[LV] ;
        if (hp <= 0)//死亡后回到泉水
        {
            sleep = 1;
            hp =10;
            weak_time = 200;//苏醒时间
            if (color == red)
            {
                x = 64;
                y = 360;
            }
            else
            {
                x = 1632;
                y = 360;
            }
        }
        if (sleep == 0/*被催眠*/)//被催眠或者死亡
    
        {
    
            //操作已经写完。。
            if (KEY_DOWN(key_left))
            {
                if (place_meet_Solid(x - speed, y, Solid, Army_Slime, Magic, dead, player_red, player_blue))
                    x -= speed;
                fx = 180;
                sprite_index = spr_left;
                move = 1;
            }
            if (KEY_DOWN(key_up))
            {
                if (place_meet_Solid(x, y - speed, Solid, Army_Slime, Magic, dead, player_red, player_blue))
                    y -= speed;
                fx = 90;
                sprite_index = spr_up;
                move = 1;
            }
            if (KEY_DOWN(key_down))
            {
                if (place_meet_Solid(x, y + speed, Solid, Army_Slime, Magic, dead, player_red, player_blue))
                    y += speed;
                fx = 270;
                sprite_index = spr_down;
                move = 1;
            }
            if (KEY_DOWN(key_right))
            {
                if (place_meet_Solid(x + speed, y, Solid, Army_Slime, Magic, dead, player_red, player_blue))
                    x += speed;
                fx = 0;
                sprite_index = spr_right;
                move = 1;
            }
            for (int i = 1; i <= 5; i++)
                if (KEY_DOWN(key_SK[i]) && LV >= i)
                {
                    if (SK[i].can_use_skill(SK_CDSUM[i], mp))//释放技能
                    {
                        SK[i].skill_use(this, Magic, object_ID);
                        mp -= SK[i].cost;
                        SK_CDSUM[i] = 0;
                    }
                }
        }
        else 
        {
            weak_time--;//苏醒
            if (weak_time<=0)
                sleep = 0;
        }
        if (my_time % 2 == 0)
            if (move)
                image_index++;//动画
        for (int i = 1; i <= 5; i++)
        {    
            if ( SK_CDSUM[i]<SK[i].CD )
                SK_CDSUM[i]++;//积累CD值
        }
        if (hp > max_hp)
            hp = max_hp;
        if (mp>max_mp)
            mp = max_mp;
        if (my_time % 2 == 0)
            if (move)
                image_index++;//动画
        image_index %= 3;
        my_time++;
    }

    伤害类

      原来我以为这个类会特别难写,因为设计了不是少技能,但是当我把这些技能一一按情况分开,再把伤害的行为步骤分类以后,也就发现只是多了一两个判断而已。

      注:部分技能描述与实际不符,请以实物为准

      

      其实构造函数没有必要有两个但是,我懒得改之前的代码,所以就将就了下。同步事件代码我就不贴了,无非也就是判断碰撞,按移动方式移动,以及一些其他的改变(比如大字爆和阳光烈焰的精灵会逐渐变大,水炮会反弹)

      其中移动方式三种,跟踪移动,小兵防御塔的攻击和水箭龟的泡泡攻击,这个移动方式每回合需要先通过目标的ID找到目标的位置。然后计算出x和y坐标分别移动多少。

      指定方向移动:这个移动方式的最多也最简单,向指定方向走几步就可以了。

      跟随使用者:或者使用者的坐标,再通过计算使自己和使用者相对坐标保持一定就可以了。

      消失方式有四种,其中跟踪的子弹要碰撞到指定目标后或目标已经死亡才会消失,其余的就是碰撞敌人或者一段时间或者两者都可以者三种方式消失。写起来也比较简单不多废话。

      这次作业是第二次打这么长的代码,还是没什么经验,并且刚开始写的时候,还没学继承多态,也没有第一次写长代码时候说改几百行就改几百行的精力和毅力,所以新学东西都没有用上,导致这次代码写得非常长。

     

      

  • 相关阅读:
    ZJCTF预赛一个.py的逆向题
    if(a)是什么意思
    整理OD一些快捷键和零碎知识点
    NSCTF-Reverse02 超级详细且简单的办法搞定
    CTF实验吧——证明自己吧
    Beat our dice game and get the flag 击败我们的骰子游戏拿到旗子
    CTF-Keylead(ASIS CTF 2015)
    【第三届强网杯】两道杂项题的wp
    【实验吧】该题不简单——writeup
    嵩天老师python网课爬虫实例1的问题和解决方法
  • 原文地址:https://www.cnblogs.com/Ike-shadow/p/9240248.html
Copyright © 2011-2022 走看看