展会信息港展会大全

2.Cocos2d-x-3.2编写3d打飞机,项目代码总结
来源:互联网   发布日期:2015-09-27 15:11:37   浏览:3638次  

导读: 1.AppDelete中applicationDidFinishLaunching代码示范 2.当电话来了时,停止恢复游戏声音的代码(在AppDelegate中加入下面代码) b...

1.AppDelete中applicationDidFinishLaunching代码示范

2.当电话来了时,停止恢复游戏声音的代码(在AppDelegate中加入下面代码)

boolAppDelegate::applicationDidFinishLaunching()

{

// initialize director

autodirector

=Director::getInstance();

autoglview

=director->getOpenGLView();

//glview->setDesignResolutionSize(480,800,ResolutionPolicy::FIXED_HEIGHT);

if(!glview)

{

intheight,width;

height

= 800;

width

=height*(640.0/960.0);

glview

=GLView::createWithRect("EarthWarrior3D",Rect(0,

0,width,height));

director->setOpenGLView(glview);

}

//设计分辨率,和实际大小时不一样的

glview->setDesignResolutionSize(640,

960, ResolutionPolicy::FIXED_HEIGHT);

// turn on display FPS

director->setDisplayStats(false);

// set FPS. the default value is 1.0/60

if you don't call this

director->setAnimationInterval(1.0

/ 60);

// create a scene. it's an autorelease

object

//auto scene = LoadingScene::createScene();

autoscene

=MainMenuScene::createScene();

//auto scene = HelloWorld::createScene();

// run

director->runWithScene(scene);

glEnable(GL_CULL_FACE);

returntrue;

}

当有一个手机电话了之后,停止游戏中的声音等的代码(在AppDelegate中加入下面代码)

// This function will be called when the app is inactive. When comes a phone call,it's be invoked too

voidAppDelegate::applicationDidEnterBackground()

{

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

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

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

// if you use SimpleAudioEngine, it

must be pause

// SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();

}

// this function will be called when the app is active again

voidAppDelegate::applicationWillEnterForeground()

{

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

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

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

// if you use SimpleAudioEngine, it

must resume here

// SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();

}

cocos2d-x-3.2中createScene(),init()函数编写

头文件:

classMainMenuScene

:publiccocos2d::Layer

{

public:

staticcocos2d::Scene*createScene();//创建场景

virtualboolinit();//初始化方法,用来添加旋转飞机,开始按钮等元素

CREATE_FUNC(MainMenuScene);//创建层

};

createScene实现代码:

Scene*MainMenuScene::createScene()

{

// 'scene' is an autorelease object

autoscene

=Scene::create();//创建一个新的场景

// 'layer' is an autorelease object

autolayer

=MainMenuScene::create();//创建本层

// add layer as a child to scene

scene->addChild(layer);//把本层添加到场景里

// return the scene

returnscene;//返回场景

}

添加对游戏手柄的支持

添加的头文件:

#include"base/CCEventListenerController.h"

#include"base/CCController.h"

在init方法中写代码时要注意:

if ( !Layer::init()

)

{

returnfalse;//如果父类初始化失败,直接返回失败

}

添加移动(MoveBy)Action的代码

auto left = MoveBy::create(2, Vec2(200,200));

//auto right = MoveBy::create(2, Vec2(-200,-200));

//_test->runAction(RepeatForever::create(Sequence::create(left, right,NULL)));

播放音乐的代码

头文件:

#include"cocos2d.h"

#include"SimpleAudioEngine.h"

USING_NS_CC;

usingnamespaceCocosDenshion;

音乐

加载音乐和音效通常是个耗时间的过程,因此为了防止由加载产生的延时导致实际播放与游戏播放不协调的现象。在播放音效和音乐前,需要预加载音乐文件。

void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath);

voidSimpleAudioEngine::preloadEffect(constchar*

pszFilePath);

因为SimpleAudioEngine与许多Cocos2d-x的部件一样,是一个单例类。所以当我们使用以上两个接口时,可以使用以下代码访问其实例:

SimpleAudioEngine::getInstance()->preloadBackgroundMusic( MUSIC_FILE );

SimpleAudioEngine::getInstance()->preloadEffect( EFFECT_FILE );

播放与停止

音频引擎提供了播放与停止的接口,以下介绍相应接口和使用方法:

//播放背景音乐,bLoop表示是否要循环播放

virtual void playBackgroundMusic(const char* pszFilePath, bool bLoop = false);

virtual unsigned int playEffect(const char* pszFilePath, bool bLoop = false,

float pitch = 1.0f, float pan = 0.0f, float gain = 1.0f); //播放音效,bLoop表示是否要循环播放

virtual void stopBackgroundMusic(bool bReleaseData = false); //停止背景音乐

virtual void stopEffect(unsigned int nSoundId); //停止指定音效,nSoundId为音效编号

virtual void stopAllEffects(); //停止所有音效

使用方法:

SimpleAudioEngine::getInstance()->playBackgroundMusic(MUSIC_FILE, true); //播放背景音乐

SimpleAudioEngine::getInstance()->stopBackgroundMusic();//停止背景音乐

SimpleAudioEngine::getInstance()->stopEffect(_soundId); //停止音效

暂停和恢复

当游戏进入后台时,通常需要暂停播放音乐,当游戏恢复前台运行时,再继续播放音乐。以下介绍几个相关接口以及用法:

virtual void pauseBackgroundMusic(); //暂停背景音乐

virtual void pauseEffect(unsigned int nSoundId); //暂停指定音效,nSoundId为音效编号

virtual void pauseAllEffects(); //暂停所以音效

virtual void resumeBackgroundMusic(); //恢复背景音乐

virtual void resumeEffect(unsigned int nSoundId); //恢复指定音效,nSoundId为音效编号

virtual void resumeAllEffects(); //恢复所有音效

使用方法:

SimpleAudioEngine::getInstance()->pauseEffect(_soundId); //暂停编号为_soundId的音效

SimpleAudioEngine::getInstance()->resumeEffect(_soundId); //恢复编号为_soundId的音效

SimpleAudioEngine::getInstance()->pauseAllEffects(); //暂停所有音效

SimpleAudioEngine::getInstance()->resumeAllEffects(); //恢复所有音效

其它成员

除了以上介绍的方法外,Cocos2d-x还提供了便捷的控制方法与属性:

virtual void setBackgroundMusicVolume(float volume); //设置背景音乐音量

virtual void setEffectsVolume(float volume);//设置音效音量

virtual void rewindBackgroundMusic();//重新播放背景音乐

virtual bool isBackgroundMusicPlaying(); //返回一个值,表示是否在播放背景音乐

预加载音乐的代码:

voidLoadingScene::LoadingMusic()

{

audioloaded

=true;//声音加载变量置为真

autoAudio

=CocosDenshion::SimpleAudioEngine::getInstance();//获取声音实例

Audio->preloadEffect("explodeEffect.mp3");//预加载音效

Audio->preloadEffect("hit.mp3");//预加载音效

Audio->preloadEffect("boom2.mp3");//预加载音效

Audio->preloadEffect("boom.mp3");//预加载音效

Audio->preloadBackgroundMusic("Orbital

Colossus_0.mp3");//预加载音效

Audio->playBackgroundMusic("Flux2.mp3");//预加载背景音乐

}

添加粒子效果的代码

//************ adds emission flare ****************

autoflare

=ParticleSystemQuad::create("missileFlare.plist");//创建料子特效,ParticleSystemQuad用法,源码

flare->setScale(5);//粒子效果放大5倍

floatoriginX

= -9.0f;//料子效果的初始旋转角度

floatoriginY

= 159.0f;//料子效果的初始旋转角度

floatoriginZ

= 9.0f;//料子效果的初始旋转角度

flare->setTotalParticles(50);//粒子效果总粒子数量

flare->setRotation3D(Vec3(-originX,-originY,-originZ));//设置粒子效果的旋转角度

flare->setPosition(-39,0);//设置粒子效果的位置

flare->setPositionType(tPositionType::GROUPED);//设置粒子效果的位置类型,跟随发射器,不跟随发射器,等各种效果

flare->setStartColor(Color4F(0,0.99,1,1));//设置粒子效果颜色

//sprite->addChild(flare,-1);

plane->addChild(flare,

-1);//把粒子效果添加到旋转飞机上,注意粒子效果的位置是相对于旋转飞的,因为旋转飞机是粒子效果的父类

autoemis

=ParticleSystemQuad::create("menuEmission.plist");//创建粒子效果

emis->setScale(3);//粒子效果放大3倍

emis->setRotation3D(Vec3(-originX,-originY,-originZ));//设置粒子效果旋转角度

emis->setPosition(-40,0);//设置粒子效果位置,相对于旋转飞机

emis->setPositionType(tPositionType::GROUPED);//旋转粒子效果位置类型

emis->setRotation(180);//设置粒子效果旋转角度

plane->addChild(emis,

-2);//添加粒子效果到飞机上

//************ adds vanishing ****************

autofileUtil

=FileUtils::getInstance();//得到文件实例

autoplistData

=fileUtil->getValueMapFromFile("vanishingPoint.plist");//得到plist文件数据,返回类型ValuMap

//auto sf = SpriteFrame::create("bullets.png",

Rect(5,8,24,32));

autovanishing

=ParticleSystemQuad::create(plistData);//创建粒子效果,用于层背景,粒子创建的别一种方式

vanishing->setAnchorPoint(Vec2(0.5f,0.5f));//设置粒子效果的锚点

vanishing->setPosition(visible_size_macro.width-90,visible_size_macro.height/2

+50);//设置粒子效果的位置

this->addChild(vanishing,1,1);//添加粒子效果到本层

创建菜单,并且添加回调的方法

//************* license *******************

autolicense_normal=Sprite::createWithSpriteFrameName("license.png");//作用同上

autolicense_pressed=Sprite::createWithSpriteFrameName("license.png");//作用同上

license_item

=MenuItemSprite::create(license_normal,license_pressed,CC_CALLBACK_1(MainMenuScene::license,this));//作用同上

license_item->setPosition(visibleSize.width/2-200,100);//作用同上

license_item->setScale(0.7);//作用同上

//************* credits ******************

autocredits_normal=Sprite::createWithSpriteFrameName("credits.png");//作用同上

autocredits_pressed=Sprite::createWithSpriteFrameName("credits.png");//作用同上

credits_item

=MenuItemSprite::create(credits_normal,credits_pressed,CC_CALLBACK_1(MainMenuScene::credits,this));//作用同上

credits_item->setPosition(visibleSize.width/2+200,100);//作用同上

credits_item->setScale(0.7);//作用同上

//************* Menu ******************

automenu

=Menu::create(startgame_item,license_item,credits_item,NULL);//创建按钮

menu->setPosition(origin);//设置按钮位置

this->addChild(menu,3);//添加按钮到本层

添加回调方法:

voidMainMenuScene::startgame(Ref*sender)

{

startgame_item->runAction(Sequence::create(ScaleTo::create(0.1f,

1.4f),

ScaleTo::create(0.1f,

1.2f),

ScaleTo::create(0.1f,

1.3f),

CallFunc::create(CC_CALLBACK_0(MainMenuScene::startgame_callback,this)),NULL));//开始按钮被点后,按钮显示放大缩小效果,并在显示完后执行开始按钮回调

}

voidMainMenuScene::startgame_callback()

{

//auto scene_test = LoadingScene::createScene();

//Director::getInstance()->replaceScene(scene_test);

//return ;

CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic();//停止播放背景音乐

GameLayer::isDie=false;//初始化游戏战斗部分的主角,未死

autoscene

= (LoadingScene::audioloaded)

? GameMainScene::createScene()

:LoadingScene::createScene();//加载资源,如果加载完成,进入游戏主界面

Director::getInstance()->replaceScene(scene);//切换场景

}

voidMainMenuScene::credits(Ref*sender){

credits_item->runAction(Sequence::create(ScaleTo::create(0.1f,

0.8f),

ScaleTo::create(0.1f,

0.6f),

ScaleTo::create(0.1f,

0.7f),

CallFunc::create(CC_CALLBACK_0(MainMenuScene::credits_callback,this)),NULL));//同开始按钮

}

voidMainMenuScene::credits_callback()

{

autolicense

=InfoLayer::create("credits_03.png");//同开始按钮

license->setAnchorPoint(Vec2(0.5f,0.5f));

license->setPosition(Vec2(visible_size_macro.width/2,visible_size_macro.height/2));

addChild(license,20);

//为了在手柄调用的回调函数的时候去除,这里设置tag为20

license->setTag(20);

license->runAction(Sequence::create(ScaleTo::create(0.2f,

1.1f),

ScaleTo::create(0.1f,

0.9f),

ScaleTo::create(0.1f,

1.0f),

NULL));

}

voidMainMenuScene::license(Ref*sender){

license_item->runAction(Sequence::create(ScaleTo::create(0.1f,

0.8f),

ScaleTo::create(0.1f,

0.6f),

ScaleTo::create(0.1f,

0.7f),

CallFunc::create(CC_CALLBACK_0(MainMenuScene::license_callback,this)),NULL));//同开始按钮

}

voidMainMenuScene::license_callback()

{

autolicense

=InfoLayer::create("LICENSE_03.png");//同开始按钮

license->setAnchorPoint(Vec2(0.5f,0.5f));

license->setPosition(Vec2(visible_size_macro.width/2,visible_size_macro.height/2));

addChild(license,20);

//为了在手柄调用的回调函数的时候去除,这里设置tag为20

license->setTag(20);

license->runAction(Sequence::create(ScaleTo::create(0.2f,

1.1f),

ScaleTo::create(0.1f,

0.9f),

ScaleTo::create(0.1f,

1.0f),

NULL));

}

11通过SpriteFrameWithFile的方式创建精灵

SpriteFrameCache::getInstance()->addSpriteFramesWithFile("loadingAndHP.plist","loadingAndHP.png");//加载资源

autoloading_bk=Sprite::createWithSpriteFrameName("loading_bk.png");//创建背景精灵

loading_bk->setPosition(Vec2(visibleSize.width/2,visibleSize.height/2));//设置背景位置

addChild(loading_bk,0);d//把背景添加到层里

通过TextureCache对的方式添加图片

voidLoadingScene::LoadingPic()

{

autoTexureCache=Director::getInstance()->getTextureCache();//获取图片缓存

TexureCache->addImageAsync("boss.png",CC_CALLBACK_1(LoadingScene::LoadingCallback,this));//把图片添加到缓存里,并设置加载成后的回调

TexureCache->addImageAsync("coco.png",CC_CALLBACK_1(LoadingScene::LoadingCallback,this));//把图片添加到缓存里,并设置加载成后的回调

TexureCache->addImageAsync("groundLevel.jpg",CC_CALLBACK_1(LoadingScene::LoadingCallback,this));//把图片添加到缓存里,并设置加载成后的回调

}

13精灵旋转

#ifndef__RotateSprite_H__

#define__RotateSprite_H__

#include"Common.h"

#include"GameEntity.h"

#include"Sprite3DEffect.h"

/*

* author:xiao qin

* copyright:v1.0

* date:2014/11/30

* project Name :3D打飞机

*

* FileName:RotateSprite.h

* description :第二个场景,旋转精灵

*/

classRotateSprite

:publicGameEntity

{

public:

CREATE_FUNC(RotateSprite);

//初始化方法,用于初始化旋转精灵的变量

boolinit();

//这里的EffectSprite3D实际上就是Sprite3D

EffectSprite3D*_rotateSprite;

};

#endif

实现代码:

#include"RotateSprite.h"

/*

* author:xiao qin

* copyright:v1.0

* date:2014/11/30

* project Name :3D打飞机

*

* FileName:RotateSprite.cpp

* description :第二个场景,旋转精灵

*/

boolRotateSprite::init()

{

_rotateSprite

=EffectSprite3D::createFromObjFileAndTexture(coconut_c3b,coco_png);

_rotateSprite->setPosition(winSize.width

/ 2, 310);

_rotateSprite->setRotation3D(Vec3(270,

0, 0));

addChild(_rotateSprite);

RotateBy*rotate

=RotateBy::create(0.5,Vec3(0,

360, 0));

_rotateSprite->runAction(RepeatForever::create(rotate));

returntrue;

}

调度器(scheduler)

继承关系

默认调度器(scheduleUpdate)

该调度器是使用Node的刷新事件update方法,该方法在每帧绘制之前都会被调用一次。由于每帧之间时间间隔较短,所以每帧刷新一次已足够完成大部分游戏过程中需要的逻辑判断。

Cocos2d-x中Node默认是没有启用update事件的,因此你需要重载update方法来执行自己的逻辑代码。

通过执行schedulerUpdate()调度器每帧执行

update方法,如果需要停止这个调度器,可以使用unschedulerUpdate()方法。

HelloWorldScene.h中添加如下代码:

void update(float dt) override;

HelloWorldScene.cpp中的代码如下:

bool HelloWorld::init()

{

...

scheduleUpdate();

return true;

}

void HelloWorld::update(float dt)

{

log("update");

}

运行结果:

cocos2d: update

cocos2d: update

cocos2d: update

cocos2d: update

自定义调度器:

游戏开发中,在某些情况下我们可能不需要频繁的进行逻辑检测,这样可以提高游戏性能。所以Cocos2d-x还提供了自定义调度器,可以实现以一定的时间间隔连续调用某个函数。

由于引擎的调度机制,自定义时间间隔必须大于两帧的间隔,否则两帧内的多次调用会被合并成一次调用。所以自定义时间间隔应在0.1秒以上。

同样,取消该调度器可以用unschedule(SEL_SCHEDULE selector, float delay)。

以下代码用来测试该调度器:

HelloWorldScene.h代码如下:

voidupdateCustom(floatdt);

HelloWorldScene.cpp代码如下:

boolHelloWorld::init()

{

...

schedule(schedule_selector(HelloWorld::updateCustom), 1.0f, kRepeatForever, 0);

returntrue;

}

voidHelloWorld::updateCustom(floatdt)

{

log("Custom");

}

在控制台你会看到每隔1秒输出一下信息:

cocos2d: Custom

cocos2d: Custom

cocos2d: Custom

cocos2d: Custom

cocos2d: Custom

我们来看下scheduler(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)函数里面的参数:

第一个参数selector即为你要添加的事件函数

第二个参数interval为事件触发时间间隔

第三个参数repeat为触发一次事件后还会触发的次数,默认值为kRepeatForever,表示无限触发次数

第四个参数delay表示第一次触发之前的延时

1单次调度器(schedulerOnce)

该调度器只会触发一次,用unschedule(SEL_SCHEDULE selector, float delay)来取消该触发器。

以下代码用来测试调度器:

HelloWorldScene.h 代码如下:

voidupdateOnce(floatdt);

HelloWorldScene.cpp 代码如下:

bool HelloWorld::init()

{

...

scheduleOnce(schedule_selector(HelloWorld::updateOnce), 0.1f);

return true;

}

void HelloWorld::updateOnce(float dt)

{

log("Once");

}

在控制台中看到的效果:

cocos2d: Once

切换场景

voidLoadingScene::GotoNextScene()

{

//goto next scene.

SpriteFrameCache::getInstance()->addSpriteFramesWithFile("gameover.plist","gameover.png");//把游戏结束时要用到资源加载到缓存里

scheduleOnce(schedule_selector(LoadingScene::RunNextScene),

1.0f);//完成资源加载后切换场景

}

voidLoadingScene::RunNextScene(floatdt)

{

this->removeAllChildren();//切换场景前移除所有子结点

autoGameMainScene=GameMainScene::createScene();//创建游戏主类,用于战斗

Director::getInstance()->replaceScene(TransitionZoomFlipX::create(1.0f,GameMainScene));//切换场景

}

重写create方法和事件处理函数

InfoLayer.h

#ifndef__Moon3d__InfoLayer__

#define__Moon3d__InfoLayer__

#include"cocos2d.h"

USING_NS_CC;

classInfoLayer

:publicLayer

{

public:

staticInfoLayer*create(constchar*type_file_name);//创建层

virtualboolinit();//初始化,主要用来处理要显示的信息

private:

constchar*type_file_name;//要显示的信息对应的文件名

voiddismiss();//信息图片消失

virtualboolonTouchBegan(Touch

*touch,Event

*event);//接受触屏事件

virtualvoidonTouchMoved(Touch

*touch,Event

*event);//接受触屏事件

virtualvoidonTouchEnded(Touch

*touch,Event

*event);//接受触屏事件

};

#endif/*

defined(__Moon3d__InfoLayer__) */

InfoLayer.cpp

#include"InfoLayer.h"

InfoLayer*InfoLayer::create(constchar*type_file_name)

{

InfoLayer

*pRet =

newInfoLayer();//创建层

pRet->type_file_name

= type_file_name;//设置要显示的信息对应的图片名字

if

(pRet &&

pRet->init())//如果层创建成功并初始化成功

{

pRet->autorelease();//把层用自动引用计数管理

}

else//如果创建失败或初始化失败

{

deletepRet;//删除层实例

pRet

=NULL;//把实例置空

}

returnpRet;//返回层实例

}

boolInfoLayer::init()

{

autowindow

=Sprite::create(type_file_name);//创建要显示的信息精灵

//window->setPosition(Vec2(visibleSize.width/2,

visibleSize.height/2));

addChild(window,1);//把信息精灵添加到层中

window->setScale(0.2f);//缩小为原来的0.2倍

window->runAction(Sequence::create(ScaleTo::create(0.2f,

1.1f),

ScaleTo::create(0.1f,

0.9f),

ScaleTo::create(0.1f,

1.0f),

NULL));//播放层放大缩小动画效果

autolistener

=EventListenerTouchOneByOne::create();//创建触摸监听,这是单点触屏监听,3.2触屏机制see

botton notes

listener->setSwallowTouches(true);//设置事件可穿透

listener->onTouchBegan

= CC_CALLBACK_2(InfoLayer::onTouchBegan,this);//设置触屏回调

listener->onTouchMoved

= CC_CALLBACK_2(InfoLayer::onTouchMoved,this);//设置触屏回调

listener->onTouchEnded

= CC_CALLBACK_2(InfoLayer::onTouchEnded,this);//设置触屏回调

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);//把监听添加到事件管理器中

returntrue;//返回初始化成功

}

voidInfoLayer::dismiss(){

this->removeFromParent();//将本层从父类中移除

}

boolInfoLayer::onTouchBegan(Touch

*touch,Event

*event)

{

this->runAction(Sequence::create(ScaleTo::create(0.2f,

0.2f),CallFunc::create(CC_CALLBACK_0(InfoLayer::dismiss,this)),NULL));//当有点击事件时,让信息精灵播放动画,播放完动画后,调用层移除回调

returntrue;

}

voidInfoLayer::onTouchMoved(Touch

*touch,Event

*event)

{

}

voidInfoLayer::onTouchEnded(Touch

*touch,Event

*event)

{

}

/************************************************************************************************************

先来看下Event的结构。

1.Event--------EventTouch,EventCustom,EventMouse,EventKeyboard,EventFocus,EventAcceleration

其中,EventTouch和

EventCustom是比较特殊的两个Event。EventTouch是3.x版本的触摸机制相关的Event,而EventCustom则是3.x自定义事件机制的基础,该机制取代了2.x版本中的NotificationCenter。

2.EventListener-------------EventListenerTouchOneByOne,EventListenerTouchAllAtOnce,EventListenerCustom,EventListenerFocus,EventListenerMouse....对应

相比于Event,Listener多了一个,因为对应的Touch被拆分成了两个Listener,一个是OneByone,一个是TouchAllAtOnce。前者是onTouchBegan等函数的Listener,后者是onTouchesBegan等函数的Listener。

1.2.EventDispatcher,EventListener,Event三者的关系

Event相当于data,EventListener包含了data与fuction的一种映射关系,而EventDispatcher相当于一个Manager,管理着EventListener,决定着Event的调用顺序。

Event中包含了type,target等信息;EventListener包含了ListenerID,相关联的Node,对应的callBack;EventDispatcher里含有各种map,vector来管理不同的Listener。

*/

赞助本站

人工智能实验室
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港