Siki_Unity_1-9_Unity2D游戏开发_Roguelike拾荒者
Unity 1-9 Unity2D游戏开发 Roguelike拾荒者
任务1:游戏介绍
Food:相当于血量:每走一步下降1,吃东西可以回复(果子10药水20),被怪物攻击会减少
中间的障碍物可以打破,人走两步僵尸走一步;走到Exit进入下一关
最外圈的过道会保证是空的,其他的随机生成(--确保主角能够到达出口)
任务2:创建工程、素材
pan.baidu.com/s/1kTYS8ez
Unity5.2.1
2D Project -- Roguelike
导入Assets.unitypackage
Sprites->所有切好的图片 -- 主角/地形/出口/围墙/僵尸等
任务3:创建游戏主角
Sprites中找到6个主角Scavengers的sprite图片,拖入Hierachy中
会自动在游戏物体Scavengers_SpritesSheet_0中创建一个动画Animator,并将Animation
和AnimationController保存在Sprites文件夹中,重命名控制器为Player/ 动画为PlayerIdle
并创建文件夹分类Assets->Animations->Animation/AnimatorController
此时,运行游戏,会自动播放Idle动画
双击AnimatorController打开动画编辑器,修改速度为0.5
创建攻击动画:
将两个攻击Sprites拖入Player物体,会自动在Player中创建一个新的动画PlayerAttack
创建受攻击动画:相似 -- PlayerUnderAttack
创建主角的Prefab
任务4:创建敌人
与创建主角相似
创建完Enemy1,创建Enemy1的Prefab
因为Enemy2的动画控制器AnimatorController与Enemy1相同
在Project视窗中右键Create->Animator Override Controller
将Enemy1的Animator赋值给新建的Animator -- 表示两个Enemy共用一个状态机
将Hierarchy中的Enemy1->GameObject->Break prefab instance,重命名为Enemy2
将Enemy2 Animator赋值给Enemy2
创建Enemy2的两个动画,将动画拖入Enemy2 Animator赋值即可
相当于:Enemy1和Enemy2共用了Enemy1的状态机(Animator),但是使用了不同动画
创建Enemy2的Prefab
任务5:创建地板/ 围墙/ 食物
创建地板:
把8种地板和出口分别拖入,并重命名Floor1~8/ Exit
把8中障碍物拖入,重命名为Wall1~8
做成Prefab
创建围墙:三种围墙,重命名为OutWall1~3并做成Prefab
创建食物:拖入Soda和Food,做成Prefab
任务6:生成地图
创建空物体GameManager
创建MapManager.cs
// 地图左下角设置为(0, 0)
public GameObject[] outWallArray/ floorArray;
// 创建根物体
private Transform mapHolder;
mapHolder = new GameObject("Map").transform;
// 初始化围墙
for x for y(...) {
GameObject cell;
if (x==0 || y==0 || x==cols-1 || y==rows-1) {
int index = Random.Range(0, outWallArray.Length);
cell = GameObject.Instantiate
(outWallArray[index], ...(x, y, 0), Quaternion.identity) as GameObject;
// as GameObject -- .Instantiate()生成的为Object类型,强制转化为GameObject
// 在(x,y,0)处生成一个旋转为0的围墙
} else ...
cell.transform.SetParent(mapHolder);
}
// 初始化地板
else {
int index = Random.Range(0, floorArray.Length);
cell = GameObject.Instantiate
(floorArray[index], ...(x,y,0), Quaternion.identity) as GameObject;
}
此时生成的地图并不位于相机正中心
-- 地图长10宽10 -- (每一格长宽为1,从(0,0)开始,即画面从(-0.5, -0.5开始))
--> Camera位置:(4.5, 4.5, 0),颜色改为黑色
private void InitMap() {
for (int x = ; x < cols; x++) {
for (int y = ; y < cols; y++) {
GameObject cell;
if (x == || x == cols - || y == || y == rows - ) {
int index = Random.Range(, outWallArray.Length);
cell = GameObject.Instantiate
(outWallArray[index], new Vector3(x, y, ), Quaternion.identity);
} else {
int index = Random.Range(, floorArray.Length);
cell = GameObject.Instantiate
(floorArray[index], new Vector3(x, y, ), Quaternion.identity);
}
cell.transform.SetParent(mapHolder);
}
}
}
任务7:控制障碍物的生成
在InitMap()中
private List<Vector2> positionList = new List<Vector2>(); // 用于取得中间部分的格子位置
positionList.Clear();
for(int x/y = 0 + 2; x/y < cols/rows - 2; x/y++) { // 围墙一列,空道一列
positionList.Add(new Vector2(x, y));
}
// 从上面的positionList中随机取得格子放入障碍物/食物/敌人
// 创建障碍物
public GameObject[] wallArray;
public int min/maxCountWall = 2/8;
int wallCount = Random.Range(minCountWall, maxCountWall + 1);
for(int i = 0~wallCount) {
// 随机取得格子位置
int positionIndex = Random.Range(0, positionList.Count); // 随机取得index
Vector2 pos = positionList[positionIndex]; // 得到位置信息
positionList.RemoveAt[positionIndex]; // 移除该位置 -- 保证一个格子只能有一个东西
// 随机取得障碍物
int wallIndex = Random.Range(0, wallArray.Length);
GameObject cell = GameObject(wallArray[...], pos, ...) as GameObject;
cell.transform.SetParent(mapHolder);
}
避免地板将障碍物覆盖,设置Layer:Background/ Items/ Roles
positionList = new List<Vector2>();
positionList.Clear();
for(int x = +; x < cols - ; x++) {
for(int y = + ; y < rows - ; y++) {
positionList.Add(new Vector2(x, y));
}
}
int wallCount = Random.Range(minCountWall, maxCountWall + );
for(int i = ; i < wallCount; i++) {
int positionIndex = Random.Range(, positionList.Count);
Vector2 pos = positionList[positionIndex];
positionList.RemoveAt(positionIndex);
int wallIndex = Random.Range(, wallArray.Length);
GameObject cell = GameObject.Instantiate
(wallArray[wallIndex], pos, Quaternion.identity) as GameObject;
cell.transform.SetParent(mapHolder);
}
任务8&9:敌人和食物的随机生成 & 代码优化
食物和敌人的数量与关卡有关 -- 数量成正比
创建GameManager.cs -- 控制游戏关卡
public int level = 1;
在MapManager.cs中
// 获取GameManager
private GameManager gameManager;
gameManager = GetComponent<GameManager>();
创建食物 -- 数量2~level*2
int foodCount = Random.Range(2, gameManager.level * 2 + 1);
// 取得随机位置 -- 重复代码写成Vector2 RandomPosition()
Vector2 pos = RandomPosition();
// 随机取得物体 -- 重复代码写成GameObject RandomPrefab(GameObject[] prefabs);
GameObject foodPrefab= Instantiate(RandomPrefab(foodArray)) as GameObject;
foodPrefab.transform.setParent(mapHolder);
// get a random available position
private Vector2 RandomPosition() {
int positionIndex = Random.Range(, positionList.Count);
Vector2 pos = positionList[positionIndex];
positionList.RemoveAt(positionIndex);
return pos;
} // get a random gameobject
private GameObject RandomPrefab(GameObject[] prefabs) {
int index = Random.Range(, prefabs.Length);
return prefabs[index];
}
创建敌人 -- 数量为level / 2
int enemyCount = gameManager.level/2;
for(0~enemyCount) {
Vector2 pos = ...;
GameObject enemyPrefab = GameObject.Instantiate(...) as GameObject;
enemyPrefab.transform.setParent(mapHolder);
}
创建出口 -- 位置固定在右上方
GameObject exit =
(Instantiate(exitPrefab, new Vector2(cols-2, rows-2), Quaternion.identity) as GameObject;
exti.transform.SetParent(mapHolder);
-- 代码优化
上面的创建代码是可重用的 -- 写成method
private void InstantiateItems(int count, GameObject[] itemArray) {
for(int i = ; i < count; i++) {
GameObject item = GameObject.Instantiate(RandomPrefab
(itemArray), RandomPosition(), Quaternion.identity) as GameObject;
item.transform.SetParent(mapHolder);
}
}
任务10:完善主角和敌人的动画状态机
Player的动画:PlayerIdle/ PlayerUnderAttack/ PlayerAttack
PlayerIdle<-->PlayerUnderAttack
PlayerIdle<-->PlayerAttack
在Animator中添加触发器Trigger分别称为Damage和Attack
Idle->Attack/UnderAttack:
将Has Exit Time取消勾选 -- 在Idle动画的任何时候都可以随时切换到另一个动画
Transition Duration = 0 -- 因为这里的动画是帧动画,因此可以进行瞬时切换成其他动画
Conditions 切换方式:添加Attack/Damage Trigger
拖动左边的箭头可以手动播放动画;拖动右边的两个箭头可以手动控制何时切换
Attack/UnderAttack->Idle:
勾选Has Exit Time即可 -- 播放完Attack/UnderAttack后自动切换到Idle动画
Transition Duration = 0 -- 瞬时间切换
Exit Time = 1 -- 退出时间(多久进行切换)
Enemy的动画:EnemyIdle/ EnemyAttack
Idle->Attack:
添加触发器Attack
Has Exit Time uncheck
Transition Duration = 0
Attack->Idle:
Has Exit Time check
Exit Time = 1
Transistion Duration = 0
检测动画:
运行游戏;选中Player/ Enemy;在Animator视窗中查看状态机
点击Trigger右边的小圆点,即为触发该Trigger
任务11:控制主角的运动
为Player添加刚体,用于控制移动,勾选Is Kinematic
为Player添加BoxCollider2D,用于检测碰撞(大小不能设置为(1,1),0.9就好)
为Player添加Player.cs来控制移动
private int posx/posy = 1; // 当前位置
private int Vector2 targetPos = new Vector2(1,1); // 目标位置
float h/v = Input.GetAxisRaw("Horizontal"/"Vertical"); // GetAxisRaw()的返回值为-1/0/1
targetPos += new Vector2(h,v); // 按键后,目标位置发生改变
private Rigidbody2D rigidbody = GetComponent<Rigidbody2D>(); // 得到刚体,用于移动
rigidbody.MovePosition(Vector2.Lerp(transform.position, targetPos, smoothing*Time.deltaTime));
// MovePosition(目标位置):向目标位置移动
// Lerp(起点,终点,速度);这里设smoothing=1
发现此时按下按钮会移动很长的距离 -- Update中不停调用targetPos += Vector2();
需要设置一个休息时间:
public float restTime = 0.5f;
public float restTimer = 0.5f; // 计时器
在Update()中
restTimer += Time.deltaTime; // 每次增加时间间隔
if(restTimer <= restTime) { // 还在休息间隔中
return; // 不进行其他操作
}
当有按键按下时:
if(h!=0 || v!=0) {
targetPos += ...; // 更新目标位置
rigidbody.MovePosition(...); // 移动
restTimer = 0; // 重置计时器
}
按下按键时,只能移动一点点距离
将rigidbody.MovePosition()代码移出if条件
因为每一帧都需要调用而不是只有按键的时候调用
发现可以一次同时在水平和竖直移动 -- 和游戏规则有悖
if(h!=0) v=0; // 优先控制竖直方向
void Update () { rigidbody.MovePosition(Vector2.Lerp(transform.position, targetPos, smooth * Time.deltaTime)); if (restTimer < restTime) {
restTimer += Time.deltaTime;
return;
} float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
if (h != ) {
v = ;
} if (h != || v != ) {
// 有按键输入时
targetPos += new Vector2(h, v);
restTimer = ; // 计时器归零
}
}
任务12:控制主角对墙体的攻击
给outWall添加BoxCollider2D,scale=0.9;添加tag = OutWall
给wall添加BoxCollider2D,scale=0.9;添加tag = Wall
在每次按键的时候,进行碰撞检测:
RaycastHit2D hit = Physics2D.Linecast(targetPos, targetPos + new Vector2(h, v));
// Physics2D.Linecast(起点,终点) 从起点向终点发射射线做检测,返回RaycastHit2D
if(hit.transform == null) { // 没有碰撞,可以行走
} else { // 有碰撞
switch(hit.collider.tag) {
case "OutWall": // 不做处理
case "Wall": // 进行攻击
// 向墙发送攻击的通知
// 在Wall里添加Wall.cs来处理行为 Wall.TakeDamage();
hit.collider.SendMessage("TakeDamage");
public int hp = ;
public Sprite damageSprite; // 受损后的墙体图片
public void TakeDamage() {
hp -= ;
GetComponent<SpriteRenderer>().sprite = damageSprite;
if(hp<=) Destroy(this.gameObject);
}
禁用自身的collider,因为可能射线会碰到自身的collider:
private BoxCollider2D collider = GetComponent<...>();
collider.enabled = false;
... // 射线检测
collider.enabled = true; // 检测后再启用
无论是移动还是攻击,都需要休息 -- 只要按下了按键,restTimer = 0;
攻击时,播放PlayerAttack动画
Player.cs
private Animator animator = GetComponent<Animator>();
animator.SetTrigger("Attack"); // 触发触发器
rigidbody.MovePosition(
Vector2.Lerp(transform.position, targetPos, smooth * Time.deltaTime));
if (restTimer < restTime) {
restTimer += Time.deltaTime;
return;
}
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
if (h != ) {v = ;}
if (h != || v != ) {
// 有按键输入时
collider.enabled = false; // 禁用自身collider
// 碰撞检测
RaycastHit2D hit =
Physics2D.Linecast(targetPos, targetPos + new Vector2(h, v));
if (hit.transform == null) {
// 没有检测到碰撞
targetPos += new Vector2(h, v);
} else {
// 检测到碰撞
switch (hit.collider.tag) {
case "OutWall":
break;
case "Wall":
hit.collider.SendMessage("TakeDamage");
animator.SetTrigger("Attack");
break;
}
}
restTimer = ; // 计时器归零
collider.enabled = true; // 检测完碰撞后 开启自身collider
}
任务13:控制主角吃食物
对食物进行碰撞检测:
对食物添加BoxCollider2D;设置为Is Trigger,scale=0.9,tag=food
存储当前食物 GameManager.public int food = 100;
将GameManager设置为单例模式
private static GameManager _instance;
public static GameManager Instance {
get{
return _instance;
}
}
private void Awake() {
_instance = this;
}
增加/减少食物的method:public void Add/ReduceFood(int count)
food +=/-= count;
case "Food":
// 用if判断hit.collider.name是Food(Clone)还是Soda(Clone)
GameManager.Instance.AddFood(10 | 20);
// 同时,需要移动Player位置 && 销毁食物
targetPos += new Vector2(h, v);
Destroy(hit.collider.gameObject);
case "Food":
targetPos += new Vector2(h, v);
Destroy(hit.collider.gameObject);
if (hit.collider.name == "Food(Clone)") {
GameManager.Instance.AddFood();
} else if(hit.collider.name == "Soda(Clone)") {
GameManager.Instance.AddFood();
}
break;
任务14&15:控制敌人的移动
当Player移动两步时,敌人移动一步
给Enemy添加BoxCollider2D进行碰撞检测,size=0.9,
给Enemy添加Rigidbody2D进行移动,勾选Is Kinematic:很重要
如果没有勾选,会导致后面Enemy被Player推走
给Enemy添加tag=Enemy
case "Enemy": // 说明所走的路径不通,判断失误,浪费一步
break;
给Enemy添加Enemy.cs
Enemy是被动移动 -- 提供移动方法,供其他对象调用
public void Move() {
// 判断Player所在位置的方向
// Transform player = GameObject.FindGameObjectWithTag(...)
Vector2 offset = player.position - transform.position;
if (offset.magnitude < 1.9f) { // 距离小于一格
// 攻击
} else { // 追 -- 哪个轴偏移大,就往哪里追
if (Mathf.Abs(offset.x) > Mathf.Abs(offset.y)) { // x轴移动
if(offset.x<0) x=-1; else x=1;
} else { // y轴移动
if(offset.y<0) y=-1; else y=1;
}
targetPos += new Vector(x, y);
}
在GameManager.cs中统一管理游戏进程:
public List<Enemy> enemyList = new List...;
在Enemy.cs中:
GameManager.Instance.enemyList.Add(this);
在GameManager.cs中控制Enemy是否行走:
bool sleepStep = true; // 是否为休息状态
OnPlayerMove() {
if(sleepStep) sleepStep = false;
else { foreach enemy in enemyList { enemy.Move();
sleepStep = true; }
}
在Player.cs中
有按键按下的时候调用OnPlayerMove()
GameManager.Instance.OnPlayerMove();
此时,Enemy会随着主角移动而移动,但是Enmey没有碰撞检测
设置目标位置targetPos之前先做检测
// 更新位置之前,做碰撞检测
collider.enabled = false; // 否则会碰撞到自己的collider
RaycastHit2D hit = Physics2D.Linecast(targetPos, targetPos+new Vector2(x,y));
collider.enabled = true;
if (hit.collider == null) {
// 没有碰撞
targetPos += new Vector2(x, y);
} else {
// 有碰撞
switch (hit.collider.tag)
{
case "Wall":
case "OutWall": // 不可能出现外墙的情况
break;
case "Food":
Destroy(hit.collider.gameObject);
targetPos += new Vector2(x, y);
break;
}
}
任务16:控制敌人的攻击
private Animator animator = GetComponent<...>();
animator.SetTrigger("Attack");
player.SendMessage("TakeDamage", lossFood); // 不同敌人的伤害不同
public void TakeDamage(int lossFood) {
GameManager.Instance.ReduceFood(lossFood);
animator.SetTrigger("UnderAttack");
}
任务17:控制游戏食物数量UI的显示
在Player每走一步就消耗一定量食物:
GameManager.Instance.ReduceFood(1);
在屏幕上显示食物:
右键创建UI->Text,重命名FoodText
字体剧中,字号25,字体为Font->PressStart2P-Regular,内容:Food: 100
放置在屏幕下方,Anchor Presets设置为bottom-center
在GameManager.cs中控制UI的更新
private Text foodText;
在void Awake()中 加入 InitGame();
void InitGame() {
foodText = GameObject.Find("FoodText").GetComponent...;
UpdateFoodText(0);
}
private void UpdateFoodText(int foodChange) {
if (foodChange == ) {
foodText.text = "Food: " + food;
} else if (foodChange > ) {
foodText.text = "+" + foodChange + " Food: " + food;
} else {
foodText.text = foodChange + " Food: " + food;
}
}
在ReduceFood()/ AddFood(){} 中加上 UpdateFoodText(count/-count);
任务18:控制游戏的失败
游戏失败:Food <= 0;
Player每走完一步:检测food的数量
-- 我的优化:在GameManager中的ReduceFood()中检测 -- 没有必要每一步都检测
if(food<=0){
GameObject.FindGameObjectWithTag("Player").SetActive(false);
// 显示游戏失败 -- UI->Text,居中,白色,字号,字体,GameOver等等
gameOverText.enabled = true;
}
任务19&20:游戏关卡的胜利判断 && 下一关卡的加载
判断Player是否到达了Exit的位置(8,8)
每次Player移动,判断是否到达终点
Player每次移动,都会调用GameManager中的OnPlayerMove()
在OnPlayerMove()中加入
// 需要得到player的targetPos,与destinationPos比较
将targetPos改为[HideInInspector]public targetPos;
Player player = GameObject.Find...Tag(...).GetComponent...;
// 还需得到Exit的位置
MapManager mapManager = GetComponent...;
if(player.targetPos.x == mapManager.cols-2 && ...y==rows-2) {
private bool missionCompleted = true;
// 加载下一关卡
Application.LoadLevel(Application.loadedLevel); // 重新加载本关卡 -- 已弃用
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
// 需要记住某些值 -- 使用系统函数OnLevelWasLoaded()
}
运行 -- 发现可以重置关卡了,但是所有数据会重置
GameManager实例不能销毁 -- 否则数据会重置
在GameManager.cs的Awake()中:
DontDestroyOnLoad(gameObject);
运行 -- 发现原来的GameManager没有销毁,但是新建了另一个GameManager
不将GameManager放在场景中
-- 将GameManager做成Prefab
-- 删除场景中的GameManager
在MainCamera中添加GameLoader.cs
// 实例化GameManager
public GameObject gameManager; void Awake() {
if(GameManager.Instance == null) {
GameObject.Instantiate(gameManager);
}
}
任务21:控制天数UI的显示
当前天数的显示
UI->Image->DayImage; alt+上下左右居中;颜色设置为黑色
在DayImage中创建一个Text叫DayText;居中偏上;字号32;字体自定义;颜色白色
什么时候显示呢? -- 初始化UI的时候需要显示
GameManage.InitGame()中:
private Image dayImage = GameObject.Find("DayImage").GetComponent<Image>();
private Text dayText ...;
dayText.text = "Day " + level;
显示完天数,需要隐藏
void HideDayImage() {
dayImage.gameObject.SetActive(false);
}
在InitGame()最后调用 Invoke("HideDayImage", 1); 即可 -- 过1s后调用
任务22:添加音效
GameManager.cs
Siki_Unity_1-9_Unity2D游戏开发_Roguelike拾荒者的更多相关文章
- 2017年Unity游戏开发视频教程(入门到精通)
本文是我发布的一个Unity游戏开发的学习目录,以后我会持续发布一系列的游戏开发教程,都会更新在这个页面上,适合人群有下面的几种: 想要做独立游戏的人 想要找游戏开发相关工作的人 对游戏开发感兴趣的人 ...
- Android游戏开发实践(1)之NDK与JNI开发03
Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...
- Android游戏开发实践(1)之NDK与JNI开发01
Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码&q ...
- Android游戏开发实践(1)之NDK与JNI开发02
Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI开发01分享完JNI的基础和简要开发流程之后,再来分享下在Android环境下的JNI ...
- [游戏开发-学习笔记]菜鸟慢慢飞(四)-Camera
游戏开发中,主相机应该是最重要的GameObject之一,毕竟游戏呈现给玩家,就是通过它. 相机的使用,在不同的游戏中,有很大的不同.这里总结一下自己学到的一些相关知识. 固定位置-游戏过程中相机的T ...
- 项目游戏开发日记 No.0x000005
14软二杨近星(2014551622) 还有一周就要交项目了, 看着周围的人也都忙碌了起来, 看着大部分人的项目都已经初具容貌, 我们团队里面也搞得人心惶惶, 一来是, 时间不多了, 还有很多事情要做 ...
- 项目游戏开发日记 No.0x000002
14软二杨近星(2014551622) 项目开发的开始, 到现在已经很久了, 软件工程的课也上了很久了, 不过, 我们的游戏现在依然还没有影子, 只能说...还是啥也不会... 从一开始, 兴致勃勃地 ...
- unity游戏开发新手-----2017年展望
0.希望三月份中旬之前找一份游戏开发的工作,必须转正; 1.希望存款3-4万; 2.今年年底结婚; 3.锻炼身体,体重保持在115斤左右,有胸肌和腹肌;(结婚之前实现) 4.技术方面: 熟练掌握C#语 ...
- Unity3D游戏开发初探—1.跨平台的游戏引擎让.NET程序员新生
一.Unity3D平台简介 Unity是由Unity Technologies开发的一个让轻松创建诸如三维视频游戏.建筑可视化.实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的 ...
随机推荐
- MFC中用户自定义类响应自定义消息
这篇技术文章不是讨论经典的MFC中的消息工作机理的,讨论消息工作原理.方式和路径的文章在网上和书本中随处可见.网上众多的讨论都是关于如何响应并进行用户自定义消息映射的:网上还有一些文章介绍如何在自定义 ...
- JedisConnectionException: Failed connecting to host localhost:6379
报错原因:没有启动服务,打开服务即可 redis.clients.jedis.exceptions.JedisConnectionException: Exception at redis.clien ...
- Python 学习笔记(九)Python元组和字典(一)
Python 元组 元组的定义 元组(tuple)是一种Python对象类型,元组也是一种序列 Python中的元组与列表类似,不同之处元组的元素不能修改 元组使用小括号,列表使用方括号 元组的创建 ...
- [HTML]在页面中输出空格的几种方式
JS中如何输出空格 在写JS代码的时候,大家可以会发现这样现象: document.write(" 1 2 3 "); 结果: 1 2 ...
- 装饰器概念&实际使用干货
定义: 本质是函数(装饰其他函数),是为其他函数添加附加功能 原则: 不能修改被装饰函数的源代码 不能修改被装饰函数的调用方式 实现装饰器知识储备: 函数及“变量” 高阶函数 把一个函数名当做实参 ...
- Eclipse新导入的项目中ctrl+点击指定方法名或类名没有反应,不能跳转问题
项目没有转成java项目 解决方法:右击项目名---选择properties----点击Project Facets,这样就可以实现ctrl+左键点击跳转了. 转成java项目后会报错 解决办法:选中 ...
- Archlinux下安装微信小程序开发工具
由于微信小程序没有Linux版本,所以需要用wine来跑 一.安装wine sudo pacman -S wine 二.安装nwjs-sdk 微信开发工具包基于nwjs-sdk #没有wget就先安装 ...
- Spring Cloud 微服务入门(一)--初识分布式及其发展历程
分布式开发出现背景 当有计算机出现一段时间之后就开始有人去想如何将不同的电脑进行网络连接,而网络连接之后对于web的项目开发就探索所谓的分布式设计,同时人们也意识到重要的数据必须多份存在.所以分布式就 ...
- sql查询关于时间的一些汇总
今天的所有数据:select * from 表名 where DateDiff(dd,datetime类型字段,getdate())=0 昨天的所有数据:select * from 表名 where ...
- node.js使用Sequelize 操作mysql
Sequelize就是Node上的ORM框架 ,相当于java端的Hibernate 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, SQLite ...