看到cocos2d-x推出了3.1版本号,真是每月一次新版本号,速度。

另一个好消息就是http://cn.cocos2d-x.org/上线了,祝贺!啥时候把我的视频和教程放上去呢?!!

视频下载地址:http://pan.baidu.com/s/1jGiLOG2  

本文介绍一款纵版射击游戏的实现,开发环境:

win7

vs2012

cocos2d-x3.0final

android adt

android ndk r9

首先看下最后的效果:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Roam9i/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

(图1。微信飞机大战执行效果)

源代码下载地址:http://download.csdn.net/detail/sdhjob/7513863

一、游戏资源准备

menu.png

首页的菜单背景

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Roam9i/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

about.png

关于界面

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Roam9i/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

help.png

帮助界面

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Roam9i/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

background3.png

游戏场景的

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Roam9i/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

end.png
 
  bullet.png aaa.png 

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2Roam9i/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">ccc.png

子弹和敌机文件

爆炸效果

(被CSDN的博客加上水印。就这样了,大家还是下源代码吧)

二、Cocos2d-x3.0项目创建和VS2012编译

2.1进入命令提示符输入:

cocos new -p com.xdl.game -d c:/users/administrator/desktop/game2014 -l cpp planegame

2.2 然后进入桌面/game2014/planegame/proj.win32

2.3使用vs2012打开

planegame.sln

2.4 按F5编译执行项目,将会出现HelloWorld的界面

2.5.把全部的资源复制到 桌面/game2014/planegame/Resources文件夹下(处理图片还有3个声音文件)

三、场景跳转和主菜单实现

3.1.改动HelloWorldScene,在init方法中加入3个菜单栏目:

auto gameItem=MenuItemFont::create("StartGame",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

auto helpItem=MenuItemFont::create("Help",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

auto aboutItem=MenuItemFont::create("About",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

gameItem->setPosition(Point(origin.x + visibleSize.width/2 - closeItem->getContentSize().width/2,200 ));

helpItem->setPosition(Point(origin.x + visibleSize.width/2 - closeItem->getContentSize().width/2,150 ));

aboutItem->setPosition(Point(origin.x + visibleSize.width/2 - closeItem->getContentSize().width/2,100 ));

gameItem->setColor(Color3B::BLACK);

helpItem->setColor(Color3B::BLACK);

aboutItem->setColor(Color3B::BLACK);

gameItem->setTag(11);

helpItem->setTag(12);

aboutItem->setTag(13);

3.2 改动菜单的回调方法MenuCallBack

void HelloWorld::menuCloseCallback(Ref* pSender)

{  MenuItem * nowItem=(MenuItem *)pSender;

SimpleAudioEngine::getInstance()->playEffect("select.wav"); //播放音乐

switch(nowItem->getTag())

{

case 10:

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)

MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");

return;

#endif

Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

exit(0);

#endif

break;

case11://game

Director::getInstance()->replaceScene(GameScene::createScene());

break;

case12://help

Director::getInstance()->replaceScene( TransitionFlipY::create(1,HelpScene::createScene()));

  break;

case13://about

Director::getInstance()->replaceScene( TransitionFlipY::create(1,AboutScene::createScene()));

break;

}

}

这样通过导演对象实现场景跳转。

3.3 在帮助。关于场景实现跳转回来

须要在项目中加入3个.h文件和3个.cpp文件,保存到Classes文件夹下(注意文件夹,不要保存错了)

HelpScene.h

HelpScene.cpp

GameScene.h

GameScene.cpp

AboutScene.h

AboutScene.cpp

若想在帮助和关于场景跳转回来须要增加触摸消息处理,见HelpScene的init方法:

bool HelpScene::init(){

if(!Layer::init())

{

return
false;

}

auto spbk=Sprite::create("help.png");

spbk->setPosition(Point::ZERO);

spbk->setAnchorPoint(Point::ZERO);

this->addChild(spbk);

EventListenerTouchOneByOne * touch=EventListenerTouchOneByOne::create();

touch->onTouchBegan=[](Touch * touch,Event * event){

return true;

};

touch->onTouchMoved=[](Touch * touch,Event * event){

Director::getInstance()->replaceScene( HelloWorld::createScene());

};

_eventDispatcher->addEventListenerWithSceneGraphPriority(touch,this);

return true;

}

四、游戏场景背景滚动

4.1首先在GameScene的init方法加入背景层,代码例如以下:

auto spbk=Sprite::create("background4.png");

spbk->setAnchorPoint(Point::ZERO);

spbk->setPosition(Point::ZERO);

this->addChild(spbk);

spbk->setTag(10);

auto spbk02=Sprite::create("background4.png");

spbk02->setAnchorPoint(Point::ZERO);

spbk02->setPosition(Point::ZERO);

spbk02->setPositionY(spbk->getPositionY()+680);

this->addChild(spbk02);

spbk02->setTag(11);

为什么要加入2遍呢?由于要实现循环的贴图效果,

4.2 在init方法计划任务

this->schedule(schedule_selector(GameScene::moveBackground),0.01);

4.3 处理计划任务

void  GameScene::moveBackground(float t)

{

auto spbk=this->getChildByTag(10);

auto spbk02=this->getChildByTag(11);

spbk->setPositionY(spbk->getPositionY()-1);

if(spbk->getPositionY()<-680)

{

spbk->setPositionY(0);

}

spbk02->setPositionY(spbk->getPositionY()+680);

}

这样就形成了循环贴图的效果,游戏游戏是横版的,还有将这样的循环贴图通过多层实现场景纵深效果(近处图层移动快,远处移动慢)

当背景相对屏幕向下移动,飞机相对屏幕不懂,但飞机相对背景则向上飞行(好多游戏的主角事实上一直在屏幕中间)

五、飞机动画和触摸控制

5.1 在init方法创建飞机动画

auto spPlane=Sprite::create();

spPlane->setTag(110);

spPlane->setPosition(Point(160,240));

this->addChild(spPlane);

Vector<SpriteFrame*> allframe;//保存动画的每一帧

for(int i=0;i<4;i++)

{

SpriteFrame * sf=SpriteFrame::create("player.png",Rect(i*47,0,47,56));

allframe.pushBack(sf);

}

Animation * ani=Animation::createWithSpriteFrames(allframe,0.1);

spPlane->runAction(RepeatForever::create(Animate::create(ani)));

5.2 通过触摸控制飞机移动

EventListenerTouchOneByOne * event=EventListenerTouchOneByOne::create();

event->setSwallowTouches(true);

event->onTouchBegan=CC_CALLBACK_2(GameScene::onTouchBegan,this);

event->onTouchMoved=CC_CALLBACK_2(GameScene::onTouchMoved,this);

event->onTouchEnded=CC_CALLBACK_2(GameScene::onTouchEnded,this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(event,this);

-------在GameScene中加入下面方法

bool GameScene::onTouchBegan(Touch *touch, Event *unused_event){

px=touch->getLocation().x;

py=touch->getLocation().y;

return true;

}

void GameScene::onTouchMoved(Touch *touch, Event *unused_event){

int mx=(touch->getLocation().x-px);

int my=(touch->getLocation().y-py);

auto spPlane=this->getChildByTag(110);

spPlane->runAction(MoveBy::create(0,Point(mx,my)));

px=touch->getLocation().x;

py=touch->getLocation().y;

}

void GameScene::onTouchEnded(Touch *touch, Event *unused_event){

}

这样就实现了在屏幕滑动改变飞机坐标。

六、子弹发射

6.1 在GameScene中加入成员Vector用来保存全部的子弹层

Vector<Sprite *> allBullet;

6.2 计划任务,定时产生子弹和移动子弹

this->schedule(schedule_selector(GameScene::newBullet),0.5);

this->schedule(schedule_selector(GameScene::moveBullet),0.01

6.3 实现产生子弹的方法和移动子弹的方法

void  GameScene::newBullet(float t){

auto spPlane=this->getChildByTag(110);

Sprite * bullet=Sprite::create("bullet3.png");

bullet->setPosition(spPlane->getPosition());

this->addChild(bullet);

this->allBullet.pushBack(bullet);

}

void  GameScene::moveBullet(float t){

for(int i=0;i<allBullet.size();i++)

{  auto nowbullet=allBullet.at(i);

nowbullet->setPositionY(nowbullet->getPositionY()+3);

if(nowbullet->getPositionY()>Director::getInstance()->getWinSize().height)

{

nowbullet->removeFromParent();

allBullet.eraseObject(nowbullet);

i--;

}

}

}

七、敌机实现

敌机实现与子弹实现类似,仅仅只是一个是向上飞,一个是向下飞。

7.1 在GameScene中加入成员Vector用来保存全部的子弹层

Vector<Sprite *> allEnemy;

7.2 加入产生敌机的任务

this->schedule(schedule_selector(GameScene::newEnemy),0.5);

this->schedule(schedule_selector(GameScene::moveEnemy),0.01)

7.3 实现敌机任务方法,这里产生2种不同类型的敌机

void  GameScene::newEnemy(float t){

Sprite * enemy=nullptr;

int num=rand()%10;//随机数0-9

if(num>=3)

{

enemy=Sprite::create("aaa.png");

enemy->setTag(1000);

}

else

{   enemy=Sprite::create("ccc.png");

enemy->setTag(2000);

}

enemy->setPosition(Point(rand()%300+10,500));

this->addChild(enemy);

this->allEnemy.pushBack(enemy);

}

void  GameScene::moveEnemy(float t){

for(int i=0;i<allEnemy.size();i++)

{  auto nowenemy=allEnemy.at(i);

nowenemy->setPositionY(nowenemy->getPositionY()-3);

if(nowenemy->getPositionY()<0)

{

nowenemy->removeFromParent();

allEnemy.eraseObject(nowenemy);

i--;

}

}

}

八、碰撞检測和加分

8.1 加入和引擎主线程fps一致的任务处理方法update

this->scheduleUpdate();

8.2 实现碰撞检測游戏逻辑

void GameScene::update(float t){

auto spPlane=this->getChildByTag(110);

Rect rp(spPlane->getPositionX(),spPlane->getPositionY(),47,56);

for(int i=0;i<allEnemy.size();i++)

{  auto nowenemy=allEnemy.at(i);

Rect er(nowenemy->getPositionX(),nowenemy->getPositionY(),40,50);

if(rp.intersectsRect(er))

{ //爆炸

newBomb(spPlane->getPositionX(),spPlane->getPositionY());

//移除敌机

nowenemy->removeFromParent();

allEnemy.eraseObject(nowenemy);

i--;

//播放音乐

SimpleAudioEngine::getInstance()->playEffect("explo.wav");

//停止全部任务和动作

//Director::getInstance()->getActionManager()->pauseAllRunningActions();

this->pauseSchedulerAndActions();

auto spover=Sprite::create("end.png");

spover->setPosition(Point::ZERO);

spover->setAnchorPoint(Point::ZERO);

this->addChild(spover);

auto act=Sequence::create(

DelayTime::create(2), //等待2秒

CallFunc::create(this,callfunc_selector(GameScene::jumpToMenu)),//运行跳转方法

NULL

);

this->runAction(act);

}

//敌机和子弹碰撞检測

for(int j=0;j<allBullet.size();j++)

{ auto nowbullet=allBullet.at(j);

Rect br(nowbullet->getPositionX(),nowbullet->getPositionY(),20,20);

if(er.intersectsRect(br))

{//改动分数

Label * labScore=(Label*)this->getChildByTag(120);

score+=nowenemy->getTag();

//爆炸效果

newBomb(nowbullet->getPositionX(),nowbullet->getPositionY());

//粒子效果

auto ps=ParticleSystemQuad::create("bomb.plist");

ps->setPosition(Point(nowbullet->getPositionX(),nowbullet->getPositionY()));

this->addChild(ps);

labScore->setString(String::createWithFormat("score:%d",score)->_string);

//移除子弹层

nowbullet->removeFromParent();

allBullet.eraseObject(nowbullet);

//移除敌机层

nowenemy->removeFromParent();

allEnemy.eraseObject(nowenemy);

i--;

//音效

SimpleAudioEngine::getInstance()->playEffect("explo.wav");

break;

}

}

}

}

九、爆炸效果

当碰撞检測到,在飞机位置产生一个新的爆炸效果层,播放动画。动画播放完毕自己主动删除自己。

void GameScene::newBomb(int x,int  y)

{

Vector<SpriteFrame*> allframe;

for(int i=0;i<7;i++)

{

SpriteFrame * sf=SpriteFrame::create("boom.png",Rect(i*44,0,44,47));

allframe.pushBack(sf);

}

Animation * ani=Animation::createWithSpriteFrames(allframe,0.03);

auto sprite=Sprite::create();

Action * act=Sequence::create(

Animate::create(ani),  //动画

CCCallFuncN::create(sprite,callfuncN_selector(GameScene::killMe)),//调用自删除方法

NULL);

this->addChild(sprite);

sprite->setPosition(Point(x,y));

sprite->runAction(act);

}

void GameScene::killMe(Node * pSender)//自删除 pSender就是sprite这里是CallFunN,会传递节点过来

{

pSender->removeFromParentAndCleanup(true);

}

十、粒子特效和音乐播放

10.1 首先使用粒子编辑器编辑粒子文件bomb.plist(详见源代码)

10.2 加载粒子层

auto ps=ParticleSystemQuad::create("bomb.plist");

ps->setPosition(Point(nowbullet->getPositionX(),nowbullet->getPositionY()));

this->addChild(ps);

(假设粒子层也须要自删除。能够參考爆炸效果)

10.3 播放音乐和音效

首先要引入声音处理头文件和命名空间

#include "SimpleAudioEngine.h"

using namespace CocosDenshion;

然后就能够使用1条语句来播放音乐和音效了

SimpleAudioEngine::getInstance()->playBackgroundMusic("game.mp3",true); //播放背景音乐

SimpleAudioEngine::getInstance()->playEffect("explo.wav"); //播放音效

十一、判定死亡

在Update方法中,眼下仅仅增加了当敌机和飞机碰撞则死亡,实际游戏中可能有多有条件

如:敌机子弹和飞机碰撞、时间计数等,

void GameScene::update(float t){

auto spPlane=this->getChildByTag(110);

Rect rp(spPlane->getPositionX(),spPlane->getPositionY(),47,56);

for(int i=0;i<allEnemy.size();i++)

{  auto nowenemy=allEnemy.at(i);

Rect er(nowenemy->getPositionX(),nowenemy->getPositionY(),40,50);

if(rp.intersectsRect(er))

{ //爆炸

newBomb(spPlane->getPositionX(),spPlane->getPositionY());

//移除敌机

nowenemy->removeFromParent();

allEnemy.eraseObject(nowenemy);

i--;

//播放音乐

SimpleAudioEngine::getInstance()->playEffect("explo.wav");

//停止全部任务和动作

//Director::getInstance()->getActionManager()->pauseAllRunningActions();

this->pauseSchedulerAndActions();

auto spover=Sprite::create("end.png");

spover->setPosition(Point::ZERO);

spover->setAnchorPoint(Point::ZERO);

this->addChild(spover);

auto act=Sequence::create(

DelayTime::create(2), //等待2秒

CallFunc::create(this,callfunc_selector(GameScene::jumpToMenu)),//运行跳转方法

NULL

);

this->runAction(act);

}

。。。

。。。

这里也使用了动作序列,先等待2秒,然后自己主动调用jumpToMenu方法跳转到主菜单

void GameScene::jumpToMenu()//ï◊™µΩ÷˜≤Àµ•

{

SimpleAudioEngine::getInstance()->stopBackgroundMusic();

Director::getInstance()->replaceScene(HelloWorld::createScene());

}

十二、移植到Android平台

12.1 eclipse导入项目

在VS2012中开发好项目之后,使用adt工具(ecplise)导入项目,import

桌面\game2014\plangame\proj.android

12.2 改动jni/android.mk文件

加入编译的文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := cocos2dcpp_shared

LOCAL_MODULE_FILENAME := libcocos2dcpp

LOCAL_SRC_FILES := hellocpp/main.cpp \

                   ../../Classes/AppDelegate.cpp \

                   ../../Classes/GameScene.cpp \

                   ../../Classes/HelpScene.cpp \

                   ../../Classes/HelloWorldScene.cpp \

                   ../../Classes/AboutScene.cpp 

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes



LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static

LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static

LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static



include $(BUILD_SHARED_LIBRARY)



$(call import-module,2d)

$(call import-module,audio/android)

$(call import-module,Box2D)

12.3 进入cmd命令提示符

进入项目文件夹

cd  c:/Users/Administrator/Desktop/game2014/planegame

编译当前项目

cocos compile -p android

(等吧。

。。最后会在c:/Users/Administrator/Desktop/game2014/planegame/proj.android/libs/armsabi/生成一个.so文件。 成功了!

)

12.4 拷贝Cocos2d-x android库文件

到 c:/Users/Administrator/Desktop/game2014/planegame/cocos/2d/platform/android/java/src/ 拷贝org目录到

c:/Users/Administrator/Desktop/game2014/planegame/proj.android/src 文件夹

在adt中刷新项目(这时候项目的错误会消除)

12.5 打包项目

使用手机数据线连接电脑,开启调试模式

能够直接通过run,来把该项目安装到手机。

之后使用android打包向导打包生成apk。

大功告成,大家最好还是投入半天时间做个《微信飞机大战》。下节课讲《2048》的开发。

Cocos2d-x 3.0final 终结者系列教程16-《微信飞机大战》实现的更多相关文章

  1. Cocos2d-x 3.0final 终结者系列教程13-贪食蛇游戏案例(全)

    快过节了.谢谢了屈原,我们爱你. 应该多几个向屈大人一样跳江的,这样我们就能够放假纪念啦. ---------------------------------快过节了.弄个案例,大家最好还是假期做做, ...

  2. Cocos2d-x 3.0final 终结者系列教程01-无论是从cocos2d-x2.x升级到版本cocos2d-x3.x

    诡谲的江湖,易变. 花花世界,车来人往. 最终确定.安家,将Cocos2d-x3.0final相关技术精加工的版本.并推出了博客文章, 不为他人,只为自己. 学习交流QQ群:301954471 --- ...

  3. Cocos2d-x 3.0final 终结者系列教程04-引擎架构分析

    从前,有一个跟我来Android学生,总是问我: 沉老师,为什么Android的形式被称为Activity,为什么要onCreate方法写setContentView(R.layout.main)? ...

  4. Cocos2d-x 3.0final 终结者系列教程12-Vector&amp;map&amp;value

    北京时间昨天下午,温40度.中午12:16我来到了篮球场点.思维1分钟决定开站 转球: 我和另一个3队友半开始, 我手中的球的那一刻我突然火爆球不稳,突然问,淡淡的味道橡胶和烧烤的味道混合. 个腾空跳 ...

  5. Cocos2d-x 3.0final 终结者系列教程03-源代码文件夹说明

    话说今天从霍营到回龙观,走到天鑫家园东路.我肋哥去,堵死我啦.7:30出门,9:10还没到回龙观. 北京这交通真是坑爹.回过头想想.怪自己走小路,有时候确实快.可有时候真堵. 堵了35分钟后果断掉头, ...

  6. Cocos2d-x 3.0final 终结者系列教程08-画图节点Node中的锚点和坐标系

    图片问答,(仅仅要回答正确,锚点和坐标系就学会了) 1.下图一共同拥有几个填充为淡黄色的实心矩形? 选择:A,2个  B, 4个 C,1个 D,16个 答案.B.4个 2.下图的4个实心矩形排列在几行 ...

  7. Cocos2d-x 3.0final 终结者系列教程15-win7+vs2012+adt+ndk环境搭建(无Cygwin)

    最终不用Cygwin 了.非常高兴 为什么要用Win7? 由于VS2012要求Win7以上系统才干安装! 为什么要用vs2012? 由于VS2012才支持C++11! 为什么要支持C++11? 由于C ...

  8. Cocos2d-x 3.0final 终结者系列教程02-开发环境的搭建

    本文主要以Mac平台和XCode5为基本系统环境和C++编程工具来介绍Cocos2d-x3.0final版的安装. 一.系统准备(预计要花掉半个月工资) MacBook Pro一台(本人的比較老.11 ...

  9. Cocos2d-x 3.0final 终结者系列教程05-AppDelegate入口类

    下面是Cocos2d-x的程序入口: class  AppDelegate : private cocos2d::Application { public: AppDelegate(); virtua ...

随机推荐

  1. [SaltStack] salt-minion启动流程

    SaltStack源码阅读 前面理了下salt-master的启动流程, 这次来看看salt-minion的启动流程. 启动salt-minion方法: /etc/init.d/salt-minion ...

  2. c#随机生成汉字、字母、数字

    /// <summary> /// 替换变量 /// </summary> /// <param name="content"></par ...

  3. cookie、session、localStorage、sessionStorage区别

    cookie.session 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份 ...

  4. Cryptography I 学习笔记 --- 总结

    在b站上大概的看完了Dan Boneh的密码学,对现代密码学总算有了一个粗浅的认识. 总算能在纸上手写RSA公式并且证明之了,蛤蛤. 总体的感触就是,现代密码学是一个非常博大精深的体系,我等程序员最重 ...

  5. 洛谷——P2383 狗哥玩木棒

    P2383 狗哥玩木棒 题目背景 狗哥又趁着语文课干些无聊的事了... 题目描述 现给出一些木棒长度,那么狗哥能否用给出的木棒(木棒全用完)组成一个正方形呢? 输入输出格式 输入格式: 输入文件中的第 ...

  6. 原生js获取元素的样式信息

    工作中经常会需要获取DOM元素的样式,之前都是通过jquery的css()方法,现在总结一下通过原生js获取元素样式的方法. obj.style js var _width = obj.style.w ...

  7. Scut游戏服务器引擎6.0.5.2发布

    1. 增加C#脚本中能引用多个C#脚本文件的支持2. 修正Web应用程序中使用C#脚本解析不到Bin目录的问题

  8. java基础篇4之注解

    1 注解的应用(jdk1.5的新特性) 一个注解相当于一个特殊的类 例子: @SuppressWarning("deprecation") @Deprecated @Overrid ...

  9. hadoop datanode节点超时时间设置

    datanode进程死亡或者网络故障造成datanode无法与namenode通信,namenode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长. HDFS默认的超时时长为10分 ...

  10. 奇怪!post提交 地址栏参数竟然可见

    转:    http://blog.csdn.net/yuebinghaoyuan/article/details/7727802 在做项目中,form标签中method="post&quo ...