一、前提:

完成前一篇的内容。

具体参考:Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(二)篇

二、本篇目标:

l  说说游戏中各种角色的动作、属性以及重构思路

l  进行代码重构让色狼大叔和女主角生动鲜活起来

三、内容:

说说游戏中各种角色的动作、属性以及重构思路

通过前两篇我们建立的一个简陋的游戏原型,但是游戏中的人物比如色狼大叔、女主角都看去来很呆板不够鲜活,比如色狼会沿着道路移动,那这个只能说是移动根本不像是在走路,没有走的动作感觉就是沿着道路在漂移,女主角也是一动不动的站那里。这样的游戏很没有乐趣,所以需要给这些游戏角色加入动作和表情,让人看去来他们是鲜活的,对这些角色进行一下简单的动画以及属性分析如下:

色狼大叔的动画:

1、          静止动画:游戏刚开始处于静止状态

2、          走路动画:沿着道路走

3、          死亡动画:当子弹击中血量消耗完时死亡消失

色狼大叔的属性:

1、          是否运动:色狼是否处于激活沿着道路行走

2、          是否死亡:是否被炮塔打死

3、          行走速度:不同色狼的行走速度不同

4、          色狼血量:不同色狼血量不同

5、          行走:沿着道路行走

女主角的动画:

1、          静止动画:游戏刚开始处于静止状态

2、          卖萌动画:不能像木头似的,就加点表情动作

3、          死亡动画:当纯洁值被色狼玷污光了就死亡了

女主角的属性:

1、          女主角贞洁值:相当于生命值

根据上面的分析我们把每个角色拆分成动画显示和业务属性逻辑两个部分,对色狼和女主角进行代码重构。

重构后大概结构如上图:

ActionSprite:属于CCSprite类,负责游戏角色精灵的动画显示,Luoli(萝莉)、DaShu(大叔)、JiaoShou(叫兽)等角色精灵均继承自ActionSprite,都属于动画显示类。

Luoli(萝莉):属ActionSprite的子类,负责女主角的动画效果展示。

DaShu(大叔)属ActionSprite的子类,负责大叔色狼的动画效果展示。

JiaoShou (叫兽)属ActionSprite的子类,负责叫兽色狼的动画效果展示。

Selang(色狼):属于CCNode类,负责色狼的行走、血量、速度、攻击等具体的业务,每个Selang都包含一个DaShu(大叔)或JiaoShou(叫兽)类的游戏精灵。并且具备最重要的行为实现沿着道路行走。

Girl(女孩):属于CCNode类,负责女主角的血量等具体的业务,每个Girl都包含一个Luoli类的游戏精灵。

进行代码重构让色狼大叔和女主角生动鲜活起来

打开项目工程按照上面的思路重点对色狼和女主角的代码实现进行重构。

色狼大叔代码重构

第一步:

新建ActionSprite.h、ActionSprite.cpp类(角色动画类),这个类继承自CCSprite负责游戏角色的动画效果显示,色狼和女孩都会是这个类的子类。

ActionSprite.h代码:

//声明一个动作状态的枚举类型
typedef enum _ActionState{
kActionStateNone = , //无状态
kActionStateIdle, //静止状态
kActionStateWalk, //行走状态
kActionStateDeath //死亡状态
}ActionState; class ActionSprite: public cocos2d::CCSprite
{
public:
ActionSprite(void);
~ActionSprite(void);
//静止
void idle();
//死亡
void death();
//行走
void walk();
//价格
CC_SYNTHESIZE(int,_price,Price);
//生命值
CC_SYNTHESIZE(float,_hp,HP);
//静止状态动作
CC_SYNTHESIZE_RETAIN(cocos2d::Action*,_idleAction,IdleAction);
//死亡状态动作
CC_SYNTHESIZE_RETAIN(cocos2d::Action*,_deathAction,DeathAction);
//行走状态动作
CC_SYNTHESIZE_RETAIN(cocos2d::Action*,_walkAction,WalkAction);
//当前动作状态
CC_SYNTHESIZE(ActionState,_actionState,ActionState);
//行走速度
CC_SYNTHESIZE(float,_walkSpeed,WalkSpeed);
//伤害值
CC_SYNTHESIZE(float,_damage,Damage);
//金钱
CC_SYNTHESIZE(float,_money,Money);
//是否有光环
CC_SYNTHESIZE(bool,_halo,Halo); };

ActionSprite.cpp代码:

ActionSprite::ActionSprite(void)
{
_price=;
_idleAction=NULL;
_walkAction=NULL;
_deathAction=NULL;
} ActionSprite::~ActionSprite(void)
{
//释放内存
CC_SAFE_RELEASE_NULL(_idleAction);
CC_SAFE_RELEASE_NULL(_deathAction);
CC_SAFE_RELEASE_NULL(_walkAction);
} //设置精灵为静止状态
void ActionSprite::idle()
{
if (_actionState != kActionStateIdle)
{
//先停止所有动作
this->stopAllActions();
//运行静止动作
this->runAction(_idleAction);
_actionState=kActionStateIdle;
}
} //设置精灵为行走状态
void ActionSprite::walk()
{
if (_actionState != kActionStateWalk)
{
//先停止所有动作
this->stopAllActions();
//运行行走动作
this->runAction(_walkAction);
_actionState=kActionStateWalk;
} } //设置精灵为死亡状态
void ActionSprite::death()
{
//先停止所有动作
this->stopAllActions();
this->runAction(_deathAction);
_actionState=kActionStateDeath;
}

第二步:

素材准备,设计2张大叔不同动作的图片,交替显示模拟色狼行走动画,完成后把图片拷贝到Resources的iphonehd文件夹中,为了适应小分辨率的手机把这个2张图片按比例缩小一半并且拷贝到Resources的iphone文件夹中。

第三步:

新建DaShu.h、DaShu.cpp类(色狼大叔动画类),这个类继承自上面的ActionSprite负责游戏色狼大叔的动画效果显示。

DaShu.h:

class DaShu : public ActionSprite
{
public:
DaShu(void);
~DaShu(void); CREATE_FUNC(DaShu);
//初始化方法
bool init();
//设置光环,拥有光环的色狼生命值加倍
void setHalo(bool halo);
};

DaShu.cpp:

bool DaShu::init()
{
bool bRet=false;
do
{
CC_BREAK_IF(!ActionSprite::initWithFile("dashu_2.png")); //设置静止状态动作
Vector<SpriteFrame *> idleFrames();
SpriteFrame *frame1=SpriteFrame::create("dashu_2.png", Rect(, , , ));
idleFrames.pushBack(frame1);
Animation *idleAnimation=Animation::createWithSpriteFrames(idleFrames,float(6.0 / 12.0));
this->setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation))); int i=;
//设置行走状态动作
Vector<SpriteFrame *> walkFrames();
for (i=;i<;i++)
{
SpriteFrame *frame2=SpriteFrame::create(CCString::createWithFormat("dashu_%d.png", i+)->getCString(), Rect(, , , ));
walkFrames.pushBack(frame2);
}
Animation *walkAnimation=Animation::createWithSpriteFrames(walkFrames,float(6.0 / 12.0));
this->setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation))); //设置死亡状态动作
Vector<SpriteFrame *> deathFrames();
SpriteFrame *frame3=SpriteFrame::create("dashu_2.png", Rect(, , , ));
deathFrames.pushBack(frame3);
Animation *deathAnimation=Animation::createWithSpriteFrames(deathFrames,float(6.0 / 12.0));
this->setDeathAction(Animate::create(deathAnimation));
//设置攻击值
this->setDamage();
//设置行走速度
this->setWalkSpeed(0.4f);
//设置生命值
this->setHP();
//设置金钱数
this->setMoney(1.0f/10.0f);
bRet=true;
} while (); return bRet;
} //设置光环
void DaShu::setHalo(bool halo)
{
if (halo)
{
//拥有光环后生命值加4倍
float h=this->getHP()*4.0f;
this->setHP(h);
} }

第四步:

新建Selang.h、Selang.cpp类(色狼类),这个类继承自CCNode游戏场景中的每一个色狼都有这个类产生,它肯定包含一个ActionSprite的色狼动画类,并且之前在MainScene.cpp的update方法中实现的色狼沿路行走代码也将转移到这个类的update方法中。

Selang.h:

#include "cocos2d.h"
#include "Waypoint.h"
#include "GameMediator.h"
#include "ActionSprite.h"
class Selang : public cocos2d::CCNode
{
public:
Selang(void);
~Selang(void);
//根据提供的spriteIndex实例化成不同的色狼
static Selang* nodeWithType(int spriteIndex);
//初始化方法
bool initWithType(int spriteIndex,bool halo);
//激活色狼
void doActivate(float dt);
//获取精灵Rect
virtual cocos2d::Rect getRect();
//设置精灵是否激活
void setActive(bool active);
//是否死亡
bool isDie;
void update(float delta);
//色狼精灵
CC_SYNTHESIZE_RETAIN(ActionSprite*,_mySprite,MySprite); private:
//精灵序号,为每种精灵编一个序号
int _spriteIndex;
GameMediator* m;
//当前精灵的位置
cocos2d::Point myPosition;
//走路速度
float walkingSpeed;
//开始路点
Waypoint *beginningWaypoint;
//结束路点
Waypoint *destinationWaypoint;
//是否激活
bool active;
//色狼高度
float myHeight;
//两个点的碰撞检测
bool collisionWithCircle(cocos2d::Point circlePoint,float radius,cocos2d::Point circlePointTwo, float radiusTwo);
};

Selang.cpp

//根据提供的spriteIndex实例化成不同的色狼
Selang* Selang::nodeWithType(int spriteIndex)
{
Selang* pRet=new Selang();
bool b=false;
if (pRet && pRet->initWithType(spriteIndex,b))
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet=NULL;
return NULL;
}
}
//初始化方法
bool Selang::initWithType(int spriteIndex,bool halo)
{
bool bRet=false;
do
{
//色狼类型index
_spriteIndex=spriteIndex;
//
m = GameMediator::sharedMediator();
//不激活
active=false;
//行走速度
walkingSpeed=0.2f;
ActionSprite* sprite=NULL;
if (spriteIndex==)//如果类型是1初始化成大叔色狼
{
sprite=DaShu::create();
sprite->setHalo(halo);
//设置速度
walkingSpeed=sprite->getWalkSpeed();
}
this->setMySprite(sprite);
//添加精灵到当前Selang中
this->addChild(_mySprite);
//计算当前色狼精灵1/2高
myHeight=sprite->getTextureRect().size.height/2.0f;
//获得路点集合中的最后一个点
Waypoint *waypoint=(Waypoint*)m->getWayPoints().back();
//设置为色狼出发点
beginningWaypoint=waypoint;
//获取出发点的下个点为色狼目标点
destinationWaypoint=waypoint->getNextWaypoint();
//获得出发点坐标
Point pos=waypoint->getMyPosition();
//对坐标进行校正提供半个身位高度
pos.add(Vec2(,myHeight));
//记录位置坐标
myPosition=pos;
//设置精灵的初始坐标
_mySprite->setPosition(pos);
//设置初始不可见
this->setVisible(false);
//把当前色狼添加到游戏的MainScene场景中显示
m->getNowScene()->addChild(this);
//启动定时器
this->scheduleUpdate();
bRet=true;
} while ();
return bRet;
} void Selang::doActivate(float dt)
{
//激活色狼
active=true;
//设置色狼可见
this->setVisible(true);
} //获取精灵Rect
Rect Selang::getRect()
{
Rect rect =Rect(_mySprite->getPosition().x - _mySprite->getContentSize().width * 0.5f,
_mySprite->getPosition().y - _mySprite->getContentSize().height* 0.5f,
_mySprite->getContentSize().width,
_mySprite->getContentSize().height);
return rect;
} //设置精灵是否激活
void Selang::setActive(bool aactive)
{
active=aactive;
this->setVisible(true);
} void Selang::update(float delta)
{
if (!active)
{
return;
}
Point destinationPos=destinationWaypoint->getMyPosition();
//提升色狼半个身位
destinationPos.add(Vec2(,myHeight));
//是否拐弯
if (this->collisionWithCircle(myPosition,,destinationPos,))
{
if (destinationWaypoint->getNextWaypoint())
{
//设置新的出发点和目标点
beginningWaypoint=destinationWaypoint;
destinationWaypoint=destinationWaypoint->getNextWaypoint();
}
}
Point targetPoint=destinationWaypoint->getMyPosition();
//提升色狼半个身位
targetPoint.add(Vec2(,myHeight));
float movementSpeed=walkingSpeed;
//计算目标点的向量
Point normalized=Point(targetPoint.x-myPosition.x,targetPoint.y-myPosition.y).getNormalized();
//根据速度和向量分别计算x,y方式上的偏移值
float ox=normalized.x * walkingSpeed;
float oy=normalized.y *walkingSpeed;
//更新色狼移动后的位置
myPosition = Point(myPosition.x + ox, myPosition.y +oy);
_mySprite->setPosition(myPosition);
}
//两个点的碰撞检测
bool Selang::collisionWithCircle(cocos2d::Point circlePoint,float radius,cocos2d::Point circlePointTwo, float radiusTwo)
{
float xdif = circlePoint.x - circlePointTwo.x;
float ydif = circlePoint.y - circlePointTwo.y;
//计算两点间的距离
float distance = sqrt(xdif * xdif + ydif * ydif);
if(distance <= radius + radiusTwo)
{
return true;
}
return false;
}

第五步:

如果运行一下那么上面的代码中Waypoint *waypoint=(Waypoint*)m->getWayPoints().back();这行应该会报错,因为GameMediator中没有提供getWayPoints()这个方法,所以我们要对GameMediator类进行修改加上这个方法,代码如下:

void  GameMediator::setWayPoints(cocos2d::Vector<Waypoint*> wayPoints)
{
_wayPoints=wayPoints;
} Vector<Waypoint*> GameMediator::getWayPoints()
{
return _wayPoints;
}

第六步:

在MainScene的init方法中把路点集合通过setWayPoints方法赋值给GameMediator,这样在Selang.cpp中就可以取到路点集合了:

……
this->wayPositions.pushBack(waypoint12);
GameMediator::sharedMediator()->setWayPoints(wayPositions);
……

第七步:

测试这个Selang类具体效果,先给MainScene添加一个void startGame(float delta)的方法,用这个方法开始游戏。

//开始游戏
void MainScene::startGame(float delta)
{
//实例化一个大叔类型的色狼
Selang* selang=Selang::nodeWithType();
//激活这个色狼
selang->setActive(true);
//设置色狼动画为行走动画
selang->getMySprite()->walk();
//取消定时器方法,保证startGame只执行一次
this->unschedule(schedule_selector(MainScene::startGame));
}

第八步:

我们在MainScene的init方法末尾处调用这个startGame的方法:

//0.5秒后调用startGame方法
this->schedule(schedule_selector(MainScene::startGame),0.5f);

到这里,把第一篇中临时添加色狼的代码删除,就可以运行测试游戏了,会看到色狼大叔一扭一扭的沿着道路靠近女主角。效果非常好,我们成功的对色狼的代码进行了重构。

女主角代码重构

第一步:

素材准备,设计4张萝莉不同动作的图片,交替显示模拟萝莉卖萌动画,完成后把图片拷贝到Resources的iphonehd文件夹中,为了适应小分辨率的手机把这个4张图片按比例缩小一半并且拷贝到Resources的iphone文件夹中。

第二步:

新建Luoli.h、Luoli.cpp类(女主角动画类),这个类继承自上面的ActionSprite负责游戏女主角的动画效果显示。

Luoli.h:

class Luoli : public ActionSprite
{
public:
Luoli(void);
~Luoli(void); CREATE_FUNC(Luoli);
bool init();
};

Luoli.cpp:

bool Luoli::init()
{
bool bRet=false;
do
{
CC_BREAK_IF(!ActionSprite::initWithFile("girl1_1.png")); //设置静止状态动作
Vector<SpriteFrame *> idleFrames();
SpriteFrame *frame1=SpriteFrame::create("girl1_1.png", Rect(, , , ));
idleFrames.pushBack(frame1);
Animation *idleAnimation=Animation::createWithSpriteFrames(idleFrames,float(6.0 / 12.0));
this->setIdleAction(CCRepeatForever::create(CCAnimate::create(idleAnimation))); //设置行走状态动作
int i;
Vector<SpriteFrame *> walkFrames();
for (i=;i<;i++)
{
SpriteFrame *frame1=SpriteFrame::create(CCString::createWithFormat("girl1_%d.png", i+)->getCString(), Rect(, , , ));
walkFrames.pushBack(frame1);
} Animation *walkAnimation=Animation::createWithSpriteFrames(walkFrames,float(6.0 / 12.0));
this->setWalkAction(CCRepeatForever::create(CCAnimate::create(walkAnimation))); bRet=true; } while (); return bRet;
}

第三步:

新建Girl.h、Girl.cpp类(女孩类),这个类继承自CCNode游戏场景中的女主角由这个类产生,它肯定包含一个ActionSprite的萝莉动画类。

Girl.h:

class Girl : public cocos2d::CCNode
{
public:
Girl(void);
~Girl(void); //根据提供的type实例化成不同的女主角
static Girl* nodeWithType(int type);
//初始化方法
bool initWithLocation(cocos2d::Point location);
//获取精灵Rect
cocos2d::Rect getRect(); private:
//萝莉精灵
CC_SYNTHESIZE_RETAIN(ActionSprite*,_mySprite,MySprite);
};

Girl.cpp

//根据提供的type实例化成不同的女主角
Girl* Girl::nodeWithType(int type)
{
Girl* pRet=new Girl();
GameMediator* m = GameMediator::sharedMediator();
Waypoint *waypoint=(Waypoint*)m->getWayPoints().front();
Point pos=waypoint->getMyPosition(); if (pRet && pRet->initWithLocation(pos))
{
pRet->autorelease();
return pRet;
}
else
{
delete pRet;
pRet=NULL;
return false;
}
} //初始化方法
bool Girl::initWithLocation(cocos2d::Point location)
{
bool bRet=false;
do
{
//实例化一个萝莉
ActionSprite *sprite= Luoli::create();
this->setMySprite(sprite);
//添加精灵到当前Gril中
this->addChild(sprite);
//设置为静止
sprite->idle();
//计算当前萝莉精灵1/2高
int myHeight=sprite->getTextureRect().size.height/2.0f;
//对坐标进行校正提供半个身位高度
location.add(Vec2(,myHeight));
sprite->setPosition(location);
//把当前女主角添加到游戏的MainScene场景中显示
GameMediator* m = GameMediator::sharedMediator();
m->getNowScene()->addChild(this,); bRet=true;
}
while ();
return bRet;
} Rect Girl::getRect()
{
Rect rect = Rect(_mySprite->getPosition().x - _mySprite->getContentSize().width * 0.5f,
_mySprite->getPosition().y - _mySprite->getContentSize().height* 0.5f+,
_mySprite->getContentSize().width,
_mySprite->getContentSize().height-);
return rect;
}

第四步:

在MainScene的 startGame(float delta)的方法中加上初始化女主角的代码。

……
//初始一个女主角
Girl* girl=Girl::nodeWithType();
//设置女主角动画为卖萌动画
girl->getMySprite()->walk();
//取消定时器方法,保证startGame只执行一次
this->unschedule(schedule_selector(MainScene::startGame));

到这里,把第一篇中临时添加女主角的代码删除,就可以运行测试游戏了,本篇的任务到此为止,本篇完成后android真机的运行效果如下:

结束语:

这个塔防游戏系列已经写了3篇了,到现在为止还没有出现炮塔,说好的炮塔呢?请期待下一篇炮塔姑娘的保护神~

作者交流QQ:

           邮箱:mymoney1001@126.com

Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(三)的更多相关文章

  1. Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(二)

    一.前提: 完成前一篇的内容. 具体参考:Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(一)篇 二.本篇目标: l  说说关于cocos2dx手机分辨率适配 l  对前一篇完成的塔防游戏原型进 ...

  2. Cocos2d-x3.x塔防游戏(保卫萝卜)从零开始(一)

    一.前提: 完成Hello Game项目的创建编译. 具体参考:Cocos2dx.3x_Hello Game项目创建篇 二.本篇目标: l  说说关于塔防游戏的想法和思路 l  实现一个简单的塔防游戏 ...

  3. Cocos2D:塔防游戏制作之旅(一)

    原文地址:http://www.raywenderlich.com/37701/how-to-make-a-tower-defense-game-tutorial 由Pablo Ruiz写的入门教程, ...

  4. Cocos2D:塔防游戏制作之旅(十八)

    在Enemy.m的getDamaged:方法只给你添加如下1行(在if条件内): [theGame awardGold:200]; 现在运行游戏你将注意到你不能放置超出你资源金币的炮塔了.当然杀死敌人 ...

  5. Cocos2D:塔防游戏制作之旅(十六)

    编译运行你的app,放置一些炮塔在你的地图上吧!你将看到炮塔在敌人移动如攻击范围时如何立即开始攻击,并且敌人的血条将随着攻击不断减少知道它们被人道毁灭!胜利即将来临了! 哦!Okay,这里只有少数细节 ...

  6. Cocos2D:塔防游戏制作之旅(二)

    一个象牙塔的视图 如果你并不熟悉此类型的游戏,塔防游戏是一个战略游戏,你需要购买和将武装塔放置在战略位置,去阻止一波又一波的敌人到达并摧毁你的基地 每一波敌人都更强,这些更强的对手有着更快的速度和对于 ...

  7. 制作一个塔防游戏 Cocos2d-x 2.1.4 (一)

    在这篇文章,将会学习到怎样制作一个塔防游戏.在这其中,学习怎样在设定的时间内出现一波波的敌人,使这些敌人沿着指定的路点前进.怎样在地图上指定的位置创建炮塔.怎样使炮塔射击敌人,怎样可视化调试路点和炮塔 ...

  8. 三国塔防游戏android源码

    三国塔防游戏源码,这个游戏源码比较完整的,因为上传有20M限制,把代码工程包分开了,主文件是TFGame,其他res大家按照安卓包加进去就行,欢迎下载并交流 ,大家可以参考一下吧.<ignore ...

  9. HTML5塔防游戏——《三国塔防》 - Yorhom's Game Box

    h3{ font-size:20px; } HTML5塔防游戏--<三国塔防> 游戏介绍: <三国塔防>是一款基于HTML5和Javascript的2D塔防游戏.游戏中除了塔防 ...

随机推荐

  1. 使用 IntraWeb (38) - TIWAppForm、TIWForm、TIWBaseHTMLForm、TIWBaseForm

    窗体 TIWAppForm 所在单元及继承链: IWAppForm.TIWAppForm 主要成员: property OnURLRequest: TOnURLRequest //响应下面的 DoUR ...

  2. 亲子之间,在于看懂,无关耐心zz

    每当有人告诉我:『你对孩子真有耐心!』时,我总会想起我的金项链,当越来越多人说的时候,我就越想找出来,我翻箱倒柜的找,越心急却越找不到,那 一条金项链从我十八岁那一年一直戴在我的脖子上一直到女儿两岁, ...

  3. 用Fragment制作的Tab页面产生的UI重叠问题

    本文出处:http://blog.csdn.net/twilight041132/article/details/43812745 在用Fragment做Tab页面,发现有时候进入应用会同时显示多个T ...

  4. Linux部署apache

    一.我们使用源码安装 官网:https://httpd.apache.org/文档:https://httpd.apache.org/docs/2.4/ 下载源码包 httpd-2.4.20.tar. ...

  5. swoole 安装

    swoole 安装: 1. 下载源代码,我下载的是1.8.6版本wget https://github.com/swoole/swoole-src/archive/1.8.6-stable.tar.g ...

  6. MyBatis学习之路之configuration配置

    1.首先讲解的是MyBatis核心配置文件configuration.xml的配置 一个完整的configuration.xml配置顺序如下: properties,settings,typeAlia ...

  7. 使用lock和condition实现的阻塞队列-字符串

    在jdk 的API中提供了一个字符串的阻塞队列 : class BoundedBuffer { final Lock lock = new ReentrantLock(); final Conditi ...

  8. SQL Server锁分区特性引发死锁解析

    锁分区技术使得SQL Server可以更好地应对并发情形,但也有可能带来负面影响,这里通过实例为大家介绍,分析由于锁分区造成的死锁情形. 前段时间园友@JentleWang在我的博客锁分区提升并发,以 ...

  9. 【Python之路Day12】网络篇之Paramiko

    paramiko模块,基于SSH用于连接远程服务器并执行相关操作. 一.安装 pip3 install paramiko 二.使用 1. SSHClient 用于连接远程服务器并执行基本命令 基于用户 ...

  10. nginx做反向代理并防盗链

    nginx做反向代理真的非常简单,只需设置location+proxy_pass即可. 防盗链配置有些复杂,需要注意的地方: 在防盗链的location中需要再设置一下proxy_pass(在这里走了 ...