大家好。

以“跳一跳”为开端,微信小游戏从今年年初起以迅雷不及掩耳盗铃儿响叮当之势席卷了用户的手机。从创意小游戏,到页游遗风的挂机游戏,一时间百花齐放。

当然,前者说是创意,其实绝大部分也就是直接把其他平台上的游戏模式搬到H5上而已,例如经典的三维弹球。

而作为物理引擎的代表作品,实现一款三维弹球作品对初学者的锻炼还是挺大的。这也是我今天写这篇小文的主要目的。

制作此游戏分为两个大的步骤,一是场景搭建,二是脚本编写。下面我们就来一起逐步完成这款小游戏。

场景搭建:游戏属于2D游戏,所以场景我们用2D精灵(Sprite)来搭建

一.砌墙

首先搭建一圈2D碰撞器作围墙,限制小球活动范围:

所有的墙都要在2D碰撞器内添加拖入弹性物理材质,上下墙不添加

二.铺路

在小球外部活动范围内搭建触发器,之后将在触发器中获得寻路效果:

可创建一个空物体进行管理

三,枪口

枪口(Muzzle)用来定位小球发射的地方,枪口有个子物体阀门(Valve),作用为阻挡已发射的小球被弹回枪口:

枪口需要添加LineRenderer组件,用来绘制瞄准线,阀门需要添加2D碰撞器

四.分数显示

创建一个空物体Score,它的子物体CurrentScore才是用来显示分数的Text:

五.小球管理

创建一个空物体Balls来管理所有的小球,使用2D精灵创建一个小球

六.关卡设置

在场景内一共搭建9*6个小格子,每个小格子都用来随机生成敌人,实行分层管理,每层6个,共9层:

注意:绿色小方框只是为了教程更加直观特意加的,实际开发时请移除,小格子并未添加除Transform以外的任何组件,因为小格子的作用就是定位,游戏运行时需要在该位置随机生成敌人;

七.菜单栏

游戏结束时自动弹出,游戏运行中按”Esc”键也可调用:

八,摄像机定位

由于整个场景都处于固定状态,所以将Canvas设为世界模式:

将摄像机改为正交模式

将固定在整个场景前:

九.预制件制作

创建若干个敌人(Enemy)预制件,添加上2D碰撞器和物理弹性材质

创建道具(Stunt)预制件

变大道具BigStunt:碰到后球会变大

复制道具CopyStunt:碰到后增加一个小球

创建小球(Ball)预制件,当碰到复制特效后创建

脚本编写:脚本不多, 域名拍卖 加上2个状态机也就11个

我们下面来一一介绍它们:

一.LevelCreate

脚本说明:随机生成底层关卡的元素。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

/// <summary>

/// 挂LevelPanel上

/// </summary>

public class LevelCreate : MonoBehaviour

{

//在编辑器中将当前分数ScoreText拖进去

public Text scoreText;

//所有种类几何体,在编辑器把所有几何体预制件拖进去

public Transform[] Enemys;

//所有种类道具,在编辑器把所有道具预制件拖进去

public Transform[] stunts;

//物体生成器,决定格子里是否生成东西(几率可自行设定)

public Transform PaneFactory()

{

int chance = Random.Range(0, 4);

if (chance < 3)          //75%不产生东西,

return null;

else                     //25%产生东西

return PaneManage();

}

//决定格子里该生产什么东西

Transform PaneManage()

{

int chance = Random.Range(0, 3);

if (chance < 2)           //66%产生几何体

return CreateEnemy();

else                      //33%产生道具

return CreateStunt();

}

//随机生成道具

Transform CreateStunt()

{

//随机产生一个道具数组索引

int index = Random.Range(0, stunts.Length);

//生成该索引处道具

return Instantiate(stunts[index]);

}

//随机生成敌人

public Transform CreateEnemy()

{

//随机产生一个几何体数组索引

int index = Random.Range(0, Enemys.Length);

//生成该索引处几何体

Transform enemy = Instantiate(Enemys[index]);

//给几何体赋一个随机颜色

enemy.GetComponent<Renderer>().material.color = new Color(Random.value, Random.value, Random.value);

//给几何体一个随机旋转角度

enemy.rotation = Quaternion.Euler(0, 0, Random.Range(0, 90));

//获取几何体子物体数字的Transform组件

Transform tf = enemy.GetComponentInChildren<Text>().transform;

//子物体不旋转

tf.rotation = Quaternion.Euler(0, 0, 0);

//获取当前分数

int score = System.Convert.ToInt32(scoreText.text);

if (score < 100) //如果当前分数不超过100分

//几何体数字在 1~9 之间随机生成

enemy.GetComponentInChildren<Text>().text = Random.Range(1, 10).ToString();

else //当前分数超过100分

//几何体数字在 1~当前分数/10 之间随机生成

enemy.GetComponentInChildren<Text>().text = Random.Range(1, score / 10).ToString();

return enemy;

}

}

二.LevelState

脚本说明:关卡状态机,表示当前游戏运行状态。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

/// <summary>

/// 关卡运行状态

/// </summary>

public enum LevelState

{

life,  //运行中

pause, //暂停

die, //游戏结束

}

三.LevelMove

脚本说明:

1.关卡移动方式,底层往上走一层,顶层回到最底层;

2.对顶层的物体进行判断,如果敌人到达顶层,游戏结束;

3.在最底层生成新的物体;

关卡运动方向

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

/// <summary>

/// 挂LevelPanel上

/// </summary>

public class LevelMove : MonoBehaviour

{

//需要做一个菜单,游戏死亡时弹出,在编辑器把死亡菜单拖进来

public GameObject deathPanel;

//游戏初始状态为存活

public LevelState levelState = LevelState.life;

//声明一个关卡集合用来管理每层关卡

List<Transform> lineList = new List<Transform>();

LevelCreate levelcreate; //声明创建物体的类

private void Start()

{

levelcreate = GetComponent<LevelCreate>(); //获取创建关卡类

lineList = GetAllChild(transform); //获取第一层子物体(关卡)添加进关卡集合中

CreateLevel(); //游戏开始时创建一次底层的物体

}

private void Update()

{

//在游戏运行时按Esc

if (levelState == LevelState.life && Input.GetKeyDown(KeyCode.Escape))

{

levelState = LevelState.pause; //游戏状态变为暂停

deathPanel.SetActive(true); //启用菜单

Time.timeScale = 0; //游戏暂停

}

//在暂停状态时按Esc

else if (levelState == LevelState.pause && Input.GetKeyDown(KeyCode.Escape))

{

levelState = LevelState.life; //游戏状态变为运行

deathPanel.SetActive(false); //禁用菜单

Time.timeScale = 1; //游戏恢复

}

}

List<Transform> GetAllChild(Transform fatherObj) //获取所有第一层子物体

{

//声明一个集合放第一层所有子物体

List<Transform> sonList = new List<Transform>();

int number = fatherObj.childCount; //获取第一层子物体数量

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

{

//将所有第一层子物体添加进集合中

sonList.Add(fatherObj.GetChild(i));

}

return sonList; //返回第一层子物体集合

}

void CreateLevel() //创建底层关卡

{

Transform last = lineList[lineList.Count - 1]; //获取底层关卡,物体将从该层产生

List<Transform> sonList = GetAllChild(last); //获取底层所有小方格

//生成一个几何体(每次创建关卡至少有一个几何体)

Transform enemy = levelcreate.CreateEnemy();

int index = Random.Range(0, last.childCount); //随机定位一个格子

enemy.position = last.GetChild(index).position; //将几何体创建在该格子内

enemy.parent = last.GetChild(index); //几何体作为该格子的子物体可随关卡层移动

//然后在其它格子里随机生成物体

for (int i = 0; i < sonList.Count; i++)

{

if (i != index) //除了刚才已经有敌人的格子外

{

//声明一个变量接受生成的物体

Transform obj = levelcreate.PaneFactory();

if (obj != null) //如果成功生产出东西

{

obj.position = sonList.position; //将该东西生产在此方格

obj.parent = sonList; //作为该方格的子物体随关卡层移动

}

}

}

}

//关卡往上走一层(第一层跳到最后)

public void LevelGetUp()

{

Vector3 tempPos = lineList[lineList.Count - 1].position; //获取最后层的坐标

//遍历所有关卡层

for (int i = lineList.Count - 1; i >= 0; i--)

{

if (i == 0) //如果是顶层

lineList.position = tempPos; //直接跳到底层

else //如果是其它层

lineList.position = lineList[i - 1].position; //移动到自己上一层

}

DestroyStunt(); //销毁顶层道具

lineList.Add(lineList[0]); //将第一层添加到集合最后

lineList.RemoveAt(0); //再移除第一层

CreateLevel(); //创建一次关卡关卡

if (Death()) //判断是否死亡

{

levelState = LevelState.die; //状态变为死亡

deathPanel.SetActive(true); //调用菜单

}

}

void DestroyStunt() //销毁顶层特技

{

//获取该层所有子物体

Transform[] lineSon = lineList[0].GetComponentsInChildren<Transform>();

for (int i = 0; i < lineSon.Length; i++) //遍历所有子物体的标签

{

if (lineSon.tag == "Stunt") //如果是道具

{

Destroy(lineSon.gameObject); //销毁该子物体

}

}

}

bool Death() //死亡判断

{

//获取顶层所有子物体

Transform[] lineSon = lineList[0].GetComponentsInChildren<Transform>();

for (int i = 0; i < lineSon.Length; i++) //遍历所有子物体的标签

{

if (lineSon.tag == "Enemy") //如果发现有几何体

return true; //直接游戏结束

}

return false; //如果一个都没有,游戏继续

}

}

四.BallState

脚本说明:小球状态机,小球会随着游戏运行改变状态,不同状态的小球具有不同属性。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

/// <summary>

/// 小球状态机,不用挂在任何物体上

/// </summary>

public enum BallState

{

Ready, //准备阶段

Battle, //战斗阶段

Bore, //上膛阶段

}

五.BallMove

脚本说明:

1小球在创景中的交互;

2.发射前的小球处于准备状态;

3.发射后的小球进入转斗状态,碰到敌人会加分,敌人会减血;

4.小球需要一个防卡住的方法;

小球卡住示意图

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

/// <summary>

/// 挂小球上

/// </summary>

public class BallMove : MonoBehaviour

{

float timer; //计时用

//初始状态为准备状态

public BallState state = BallState.Ready;

//碰撞时调一次,用于打击几何体(敌人)

private void OnCollisionEnter2D(Collision2D collision)

{

if (state == BallState.Battle) //如果在战斗阶段

{

GetComponent<Rigidbody2D>().gravityScale = 1; //碰到东西后重力为1

if (collision.gameObject.tag == "Enemy") //如果碰到敌人

{

//获取敌人数字

Text enemyNumber = collision.transform.GetChild(0).GetComponent<Text>();

//获取当前分数

Text Score = GameObject.Find("ScoreText").GetComponent<Text>();

if (tag == "BigBall") //如果自己是大球

{

//敌人数字-2

enemyNumber.text = ((System.Convert.ToInt32(enemyNumber.text)) - 2).ToString();

//当前分数+2

Score.text = ((System.Convert.ToInt32(Score.text)) + 2).ToString();

}

else //如果自己是小球

{

//敌人数字-1

enemyNumber.text = ((System.Convert.ToInt32(enemyNumber.text)) - 1).ToString();

//当前分数+1

Score.text = ((System.Convert.ToInt32(Score.text)) + 1).ToString();

}

}

}

}

//碰撞时持续调用,防止小球被卡住

private void OnCollisionStay2D(Collision2D collision)

{

if (collision.gameObject.tag == "Enemy") //如果碰撞的是敌人

{

timer += Time.deltaTime; //开始计时

if (timer > 1) //一秒后还停留在那上面

{

switch (Random.Range(0, 4)) //随机方向弹开

{

case 0:

GetComponent<Rigidbody2D>().AddForce(transform.up * 0.01f);

break;

case 1:

GetComponent<Rigidbody2D>().AddForce(-transform.up * 0.01f);

break;

case 2:

GetComponent<Rigidbody2D>().AddForce(transform.right * 0.01f);

break;

case 3:

GetComponent<Rigidbody2D>().AddForce(-transform.up * 0.01f);

break;

}

}

}

}

//离开碰撞时调一次

private void OnCollisionExit2D(Collision2D collision)

{

timer = 0; //计时归零

}

private void Update()

{

transform.Rotate(0, 0, 0.0001f); //物体处于非完全静止状态,持续碰撞才会生效

switch (state)

{

case BallState.Bore: //上膛阶段

GetComponent<Rigidbody2D>().gravityScale = 0; //重力变为0

break;

case BallState.Ready: //准备阶段

GetComponent<CircleCollider2D>().isTrigger = false; //关闭触发

GetComponent<Rigidbody2D>().Sleep(); //小球停止不动

break;

}

}

}

六.BallFindWay

脚本说明:

1.发射后的小球会寻路回到枪口,给每一个寻路碰撞器挂一个;

2.碰到顶上的碰撞器后.小球会进入上膛阶段,直接回到枪口;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

/// <summary>

/// FindTheWays下的所有寻路碰撞器都挂一个

/// </summary>

public class BallFindWay : MonoBehaviour

{

public Transform muzzle; //在编辑器把枪口拖进去,我们需要枪口的坐标

public float boreSpeed=0.2f; //上膛速度

private void OnTriggerStay2D(Collider2D ball) //触发时持续调用

{

//获取小球的刚体

Rigidbody2D r2d = ball.GetComponent<Rigidbody2D>();

switch (name) //根据寻路碰撞器的名字决定施加力的方向

{

case "LeftDown":

r2d.AddForce(-transform.right * 0.002f);

break;

case "RightDown":

r2d.AddForce(transform.right * 0.003f);

break;

case "Left":

case "Right":

r2d.AddForce(transform.up * 0.002f);

break;

case "Up":

//启动协程寻路(上膛)

StartCoroutine(MoveToMuzzle(ball.transform, muzzle));

//打开小球触发器,使小球能越过枪口阀门

ball.GetComponent<CircleCollider2D>().isTrigger = true;

break;

}

}

//使用协程寻路让小球朝枪口处移动

public IEnumerator MoveToMuzzle(Transform ball, Transform muzzle)

{

ball.GetComponent<BallMove>().state = BallState.Bore; //小球状态改为上膛状态

while (ball.GetComponent<BallMove>().state == BallState.Bore) //如果是上膛状态

{

//小球往枪口处寻路,完成上膛

ball.position = Vector3.MoveTowards(ball.position, muzzle.position, boreSpeed * Time.deltaTime);

yield return new WaitForFixedUpdate(); //每次循环间隔1帧

//如果小球位置和枪口位置接近

if ((ball.position - muzzle.position).sqrMagnitude <= 0.001f)

{

ball.GetComponent<BallMove>().state = BallState.Ready; //小球进入准备阶段

ball.position = muzzle.position; //将小球定在枪口位置

}

}

}

}

七.Aim

脚本说明:

1.把当前小球依次发射出去,并将发射的小球变为战斗状态;

2.小球发射方向是从枪口出发,往鼠标所在方向,显示瞄准线;

3.限制小球发射返回,不能往上发射,需要做两个限制点;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

public class Aim : MonoBehaviour //挂枪口Muzzle上

{

public GameObject balls; //把Balls拖进去

Rigidbody2D[] allBall; //声明一个数组用来管理所有小球

LineRenderer aimLine; //声明瞄准线

public Transform CriticalPointLeft; //把左边界拖进去

public Transform CriticalPointRight; //把右边界拖进去

public float shootingSpeed = 3.5f; //小球发射速度

public GameObject levelPanel; //把LevelPanel拖进去

bool levelStop; //判断关卡是否已上升

void Start()

{

Time.timeScale = 1; //游戏时间正常

allBall = balls.GetComponentsInChildren<Rigidbody2D>();//初始化(获取当前所有小球)

aimLine = GetComponent<LineRenderer>(); //获取枪口上的LineRenderer组件

}

void Update()

{

//当游戏状态为活着时

if (levelPanel.GetComponent<LevelMove>().levelState == LevelState.life)

{

if (Homing()) //所有小球都进入准备状态了

{

allBall = balls.GetComponentsInChildren<Rigidbody2D>(); //再次获取所有小球

if (levelStop) //关卡处于未上升状态

{

levelPanel.GetComponent<LevelMove>().LevelGetUp(); //调用关卡上升方法

levelStop = !levelStop; //关卡处于已上升状态

}

else

AimLaunch(); //关卡上升完成后可进行瞄准发射

}

}

}

//判断所有小球是否都进入准备状态

public bool Homing()

{

//发现任何小球不在准备状态都返回False

for (int i = 0; i < allBall.Length; i++)

{

if (allBall.GetComponent<BallMove>().state != BallState.Ready)

return false;

}

return true; //未发现不在准备状态的小球,返回True

}

void AimLaunch() //瞄准发射

{

if (Input.GetMouseButtonDown(0)) //点击鼠标左键

{

aimLine.SetPosition(0, transform.position); //在枪口处生成瞄准线起点

}

if (Input.GetMouseButton(0)) //按住鼠标左键不放

{

//获取鼠标坐标

Vector3 v = Camera.main.ScreenToWorldPoint(Input.mousePosition);

//限制瞄准范围

v = DirectionRestriction(v, CriticalPointLeft, CriticalPointRight);

//将被限制过的鼠标坐标实时给瞄准线结束点

aimLine.SetPosition(1, new Vector2(v.x, v.y));

}

if (Input.GetMouseButtonUp(0)) //抬起鼠标左键

{

StartCoroutine(LineLaunch(transform.position)); //启动协程发射小球

aimLine.SetPosition(1, transform.position); //让结束点和起点重合(撤销瞄准线)

levelStop = !levelStop; //关卡标记为可上升状态

}

}

IEnumerator LineLaunch(Vector3 muzzlePos) //用协程排队发射小球

{

Vector3 pos1 = aimLine.GetPosition(1);//获取瞄准线结束点坐标

Vector3 directionAttack = (pos1 - muzzlePos).normalized;//获取瞄准结束点与枪口的方向向量

for (int i = 0; i < allBall.Length; i++) //挨个发射小球

{

//被发射的小球变为战斗状态

allBall.GetComponent<BallMove>().state = BallState.Battle;

//球往瞄准结束点方向寻路移动

allBall.AddForce(directionAttack * shootingSpeed * Time.deltaTime);

yield return new WaitForSeconds(0.1f); //每隔0.1秒发射一个

}

}

//限定枪口瞄准方向

Vector3 DirectionRestriction(Vector3 v, Transform left, Transform right)

{

//最左不能左过左边界

if (v.x < left.position.x)

v.x = left.position.x;

//最右不能右过右边界

if (v.x > right.position.x)

v.x = right.position.x;

//高度不能超过边界

if (v.y > left.position.y)

v.y = left.position.y;

return v; //返回被限制后的坐标

}

}

八.DeathBalance

脚本说明:

1.按暂停或游戏结束时会弹出的菜单面板;

2.菜单面板会显示本局分数和最高分数,有重启游戏和退出游戏的按钮;

using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.SceneManagement;

using UnityEngine.UI;

public class DeathBalance : MonoBehaviour //挂死亡面板DeathPanel上

{

public Text score; //把当前分数ScoreText拖进去

public Text bureauScore; //把本局分数BureauScore拖进去

public Text topScore; //把最高分数TopScore拖进去

private void OnEnable()

{

bureauScore.text = score.text; //结算本局分数

if (PlayerPrefs.HasKey("分数")) //如果已经存储了分数

topScore.text = PlayerPrefs.GetString("分数");  //就获取上次存的最高分数

//让本局分数和最高分数比较,如果本局分数比最高分数大

if (Convert.ToInt32(bureauScore.text) > Convert.ToInt32(topScore.text))

{

topScore.text = bureauScore.text; //更新最高分数

PlayerPrefs.SetString("分数", bureauScore.text); //存储最高分数

}

}

public void RestartGame() //重新开始,挂按钮RestartGame上

{

SceneManager.LoadScene("ElasticBall"); //加载场景(提前保存一个场景)

}

public void QuitGame() //退出游戏,挂按钮QuitGame上

{

Application.Quit();

}

}

九.Enemy

脚本说明:挂几何体(敌人)身上,实时监测自身血量,血量<=0时自动销毁。

using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

/// <summary>

/// 挂每个几何体上

/// </summary>

public class Enemy : MonoBehaviour

{

Text number; //声明数字

private void Start()

{

number = GetComponentInChildren<Text>(); //找到子物体(数字)

}

private void Update()

{

if (Convert.ToInt32(number.text) < 1) //如果数字小于1时

Destroy(gameObject); //销毁几何体自身

}

}

十.BigBall

脚本说明:小球碰到后直径会变大20%,攻击力翻倍,标签也会更改,永久效果,且只能变大一次。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

/// <summary>

/// //挂变大道具上

/// </summary>

public class BigBall : MonoBehaviour

{

private void OnCollisionEnter2D(Collision2D collision) //被碰撞是调用

{

if (collision.gameObject.tag != "BigBall") //当普通小球碰到时

{

collision.transform.localScale *= 1.2f;//小球变大20%成打球

collision.gameObject.tag = "BigBall"; //大球的标签设为“BigBall”

}

Destroy(gameObject); //销毁变大道具

}

}

十一.CopyBall

复制道具:小球或打球碰到后会从预支件创建一个小球供玩家调配,

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

/// <summary>

/// 挂复制道具上

/// </summary>

public class CopyBall : MonoBehaviour

{

//小球需要做一个预制件拖进来

public Transform ball;

private void OnCollisionEnter2D(Collision2D collision)//被小球碰到时调用

{

//获取小球transform组件

Transform tf = collision.transform;

//在小球位置复制一个新小球(小球预制件)

Transform newBall = Instantiate(ball, tf.position,tf.rotation);

//新小球认小球的父物体“Balls”为自己的父物体

newBall.parent = tf.parent;

//新小球往右跳

newBall.GetComponent<Rigidbody2D>().AddForce(transform.right * 0.02f);

//旧小球往左跳

tf.GetComponent<Rigidbody2D>().AddForce(-transform.right * 0.02f);

//销毁复制道具

Destroy(gameObject);

}

}

步骤全部完毕,代码中一些参数可随个人喜好随意设定,如预制件生成几率,瞄准发射范围,枪口位置,关卡格子布局等等。

游戏基本就做好啦。我们来看一下运行的效果:

Unity快速上手系列之2:2D物理弹球相关推荐

  1. 视频教程-Unity快速入门系列课程(第1部)-Unity3D

    Unity快速入门系列课程(第1部) 二十多年的软件开发与教学经验IT技术布道者,资深软件工程师.具备深厚编程语言经验,在国内上市企业做项目经理.研发经理,熟悉企业大型软件运作管理过程.软件架构设计理 ...

  2. 视频教程-Unity快速入门系列课程(第2部)-Unity3D

    Unity快速入门系列课程(第2部) 二十多年的软件开发与教学经验IT技术布道者,资深软件工程师.具备深厚编程语言经验,在国内上市企业做项目经理.研发经理,熟悉企业大型软件运作管理过程.软件架构设计理 ...

  3. thinkcmf5调用指定分类的二级_Tengine快速上手系列教程amp;视频:基于Python API的图片分类应用入门丨附彩蛋...

    前言:近期,Tengine团队加班加点,好消息接踵而来,OpenCV 4.3.0发布,OPEN AI LAB AIoT智能开发平台Tengine与OpenCV合作共同加速边缘智能,Tengine再获业 ...

  4. spring cloud 快速上手系列 -> 02-配置中心 Config -> 022-Config客户端

    spring cloud 快速上手系列 系列说明:快速上手,一切从简,搭建一个简单的微服务框架,让新手可以在这个基础框架上做各种学习.研究. 02-配置中心 Config 022-Config客户端 ...

  5. 【快速上手系列】使用Springboot集成Swagger2的简单使用测试

    [快速上手系列]使用Springboot集成Swagger2的简单使用测试 简介 Swagger2是为了解决企业中接口(api)中定义统一标准规范的文档生成工具. 尤其是前后端分离时对一些业务接口也不 ...

  6. spring cloud 快速上手系列 -> 04-网关 Gateway -> 041-空的工程

    spring cloud 快速上手系列 系列说明:快速上手,一切从简,搭建一个简单的微服务框架,让新手可以在这个基础框架上做各种学习.研究. 04-网关 Gateway 041-空的工程 1,说明 网 ...

  7. 【快速上手系列】保姆级Layuimini与SSM的联合使用教程(数据表格操作)

    [快速上手系列]保姆级Layuimini与SSM的联合使用教程(数据表格操作) 使用步骤 导入layuimini 下载layuimini文件 这个并不是直接运行的,需要用HBuilder导入 layu ...

  8. 【快速上手系列】五分钟即可学会的easyUI的简单使用教程

    [快速上手系列]五分钟即可学会的easyUI的简单使用教程 一个简单方便的前端框架 引入文件 引入两个css样式和三个js <!--引入easyUI的样式 --> <link hre ...

  9. c语言 字符串转运算符,快速上手系列-C语言之基础篇(二)数据类型与运算符...

    在上一篇文章<快速上手系列-C语言之基础篇(一)>中写了关于C语言的程序结构,关键字及控制语句.本篇主要写写C语言中数据类型,以及运算符相关方面的知识. 一.变量与常量 1.常量:在程序运 ...

最新文章

  1. String,StringBuffer和StringBuilder的区别
  2. Python 3.8.0 正式发布,主要更新内容介绍
  3. 命令行下的FTP使用详解
  4. 112. Leetcode 673. 最长递增子序列的个数 (动态规划-子序列问题)
  5. MySQL默认值(DEFAULT)
  6. 【转】android Notification 的使用
  7. 【转载保存】Lucene7.1.0超详细的使用文档
  8. html中dom和bom,区分BOM和DOM,区分window、document、html、body
  9. 开发缺点_利用模板去建设开发企业网站好不好?模板建站的缺点?
  10. JavaScript:递归实现深拷贝
  11. 《HBase权威指南》读书笔记(一)
  12. 全国计算机一级成绩分配,计算机一级ms分值分配
  13. Mac 快速打开终端快捷键
  14. 多进程爬取Unsplash网站图片
  15. 用python爬取堆糖图片-小白级
  16. Python的scrapy之爬取6毛小说网
  17. Adobe Acrobat DC无需注册登陆版
  18. 上传App Store的截图尺寸
  19. 使用Docker安装HomeAssistant
  20. 【LeetCode04】最接近的三数之和

热门文章

  1. 怎样用grep匹配多个字符串? grep -E 'aaaaa|bbbbbb'
  2. 【汇正财经】沪深创冲高回落
  3. 服务器ups运行时间,图文告诉你关于UPS电源的一些基础知识
  4. 关键词展现量低时怎么办?
  5. 如何成为一个程序员:短,全面,个人摘要
  6. java osta_Java语言程序设计_科泰计算机学校用户致胜口碑致远
  7. centos7查看硬件温度,如cpu
  8. ADA开发环境的建立
  9. 为Lazada商家量身定做的精细化运营数据分析软件,Ushop BI
  10. iCAN大赛520支创新项目向世界展示“中国智造”