技术标签: unity 3d # Unity3D之日常开发 游戏引擎
推荐阅读
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
写今天这篇文章的缘由,其实是来自于前段时间和粉丝的一个聊天,最近他打算参加游戏创作大赛,问我需要准备学习什么知识,以及参加比赛的注意事项一类:
我相信因为热爱游戏而前来投身于U3D学习的粉丝想来不是少数,兴趣可以驱动学习,在完善自己心爱游戏的过程中,要不断的去学习,不断的提高自己。
而参与游戏设计比赛,更是提高自身技术实力、增长眼界见识的优秀途径,在此途中也能遇见不少志同道合的好朋友。
那今天就借此简单的聊一下,制作一个简单游戏需要具备哪些技术栈,以及参加这类比赛需要的知识基础。
并且,还有休闲类游戏《2048》的实战开发内容。
接下来就聊一下做一个游戏需要具备哪些技术栈, 因为只是面对“做出游戏”这个特定需求,所以有些技术没有列举出来,在开发中遇到可以再学习。
Unity3D安装
首先,学习Unity3D你要安装Unity3D编辑器吧,激活Unity Hub许可证。
分享 几个下载链接:
Unity3D中国版官网
Unity3D国际版官网
Unity3D下载链接
当然,比较推荐是用Unity Hub下载安装:
安装后激活许可证,这个网上教程比较多,这里就不赘述了:
Unity3D编辑器布局学习
学习Unity3D编辑器,要先熟悉Unity编辑器的布局,比如菜单栏,工具栏在什么位置:
Unity入门组件学习
Unity的特色就是面向组件编程,所以对于组件的熟悉是基本的要求。
比较常用的组件有:
组件比较多,但是不需要全部学习,只需要在用到的时候查看一下Unity3D手册即可:
Unity3D中文手册https://docs.unity.cn/cn/current/Manual/index.html
C#语言基础
Unity3D的脚本语言是C#,随意要做好Unity3D开发,C#编程语言肯定要学的。
最基本的面向对象思想,数据类型、流程控制、数组、函数都要有一定的了解。
Unity3D-UGUI
UGUI是Unity3D的UI系统,可以用UGUi搭建界面,然后很多的用户交互逻辑都是在UI上完成的,所以学好UGUI是很有必要的,下面就来看一下UGUI的基本组件:
数据结构
数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素的集合。
简单点说就是专门用于数据存储和检索的类。
因为做游戏开发或者其他项目都会用到对数据的操作,而数据结构就是专门用于数据存储和检索的类。
常见的数据结构有:
总结的技术栈看着字没有那么多,但是内容还是很多的,比如说Unity3D的组件、C#脚本语言、UGUI系统,每一块拿出来都是很多的内容,那有没有比较推荐的书籍学习呢?
《Unity 2017从入门到精通》
首先,比较推荐的就是官方的《Unity 2017从入门到精通》,这本书是对Unity3D编辑器的知识讲解比较全面,各个方面都会涉及,比如说粒子系统、地形系统、AssetBundle都会涉及。
内容上比较通俗易懂,看这本书可以对Unity的基本操作会有一些了解,还可以查漏补缺,看还有那些方面不够的。
《21天学通C#》
这本书的特点就是,简单易懂,没有那么多深奥的东西,且里面的每一个例子都非常精华,直扑主题。
《图解C#》
这本书利用图文的形式详细的讲述了程序运行时内存的分配状况,这便能让你最一些底层的东西有了一定的了解,这便扩展了你的编程思维。
《C#数据结构》
这本书对于数据结构讲解的非常详细,可以根据书籍和视频配合学习,毕竟难度还是有的。
既然本期的主题还是应对比赛,那么我们对于比赛本身也要有相应的了解和针对(也就是所谓的审题),就以粉丝这次要参加的B站游戏设计大赛为例(活动网址:https://game.bilibili.com/awards2022/):
个人分析下来,此次赛事,是通过比赛的方式激发对游戏创作的激情,发掘游戏相关人才、作品、创意,给真正喜欢游戏的人提供平台。
就算是暂时缺少技术能力,但是有游戏独到创意的人,也有参赛机会,分别以游戏包体、概念演示视频方式参赛,分区评比。
同时,赛事设有创意人气类奖项,评选交由玩家投票、互动决定。
可以看出,比赛更多着重的是鼓励好创意,也为好创意谋求更多曝光机会。好的创意、作品会得到资源支持,签约发行,作品推广等。
当然,例如比赛日程和参赛规则之类的也需要注意一下,以适配自己的项目进度和方向。
在规划好日程和作品方向后,下面就实现一个休闲类游戏《2048》,抛砖引玉一下。
开局是一个4X4棋盘,棋盘中随机出现两个数字,出现的数字为2或4。
玩家通过选择上下左右四个反向,若玩家选择的方向上有相同的数字则合并,若选择的反向上没有相同的数字但是有空位则移动,可以移动的同时合并,但是不可以连续合并。
若棋盘被数字填满,无法进行移动或合并,则游戏失败。
如果棋盘出现2048,则游戏胜利:
根据规则,来分析一下游戏的实现。
以向左移动为例:
可以看到移动后的规则:
(1)新建项目我用的Unity 2019.4.7f1
版本,模板选择2D,设置项目名称和位置:
(2)将图片另存为,然后导入到项目中:
(3)在Inspector视图中导入设置:
(4)分割图片,点击Sprite Editor后,会出现分割窗口,然后点击Slice,选择Grid By Cell Count,点击Apply:
分割后:
(5)切完后,把图集放到Resources文件夹中,没有就新建一个,Resources文件夹在Unity中有特殊作用,可以通过脚本Resources类进行访问。
PS:注意图集的名字2048,稍后代码会用到。
接着将2048_0拖入场景中,重命名为BG,设置排序层Order inLayer为-1,这样背景就总是显示在最下面了:
将BG拖入Project视图,做成预制体:
同样的操作,将2048_1拖入场景,然后重命名为Card,设置排序层为0,然后做成预制体:
将场景中的BG和Card对象删除,我们将在后面使用实例化新建出来。
至此,我们的场景就搭建完成了,下面进行代码的编写。
(1)首先,我们需要创建16个小方格作为背景,新建脚本GameManager.cs,双击打开脚本,编写代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject bgSprite;//背景卡片
private Vector2 BeginPos = new Vector2(-1.5f, 1.5f);//差不多在屏幕中间
private float OffsetX = 1.1f;//xy 加个0.1 有个间隙
private float OffsetY = 1.1f;
void Start()
{
CreateBG();
}
void CreateBG()
{
GameObject BG = new GameObject("BG");//创建空游戏对象作为背景预制体
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
Vector2 newPos = new Vector2(BeginPos.x + j * OffsetX, BeginPos.y - i * OffsetY);
Instantiate(bgSprite, newPos, Quaternion.identity, BG.transform);
}
}
}
}
将脚本附给Main Camera对象,然后将预制体BG拖到Card脚本组件的BgSprite卡槽中:
运行程序:
(2)接下来创建一个Card.cs脚本,主要用来管理创建的Card对象,找到Project视图中的Card对象,将Card.cs脚本附加上去:
修改Card.cs脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Card : MonoBehaviour
{
public Sprite[] CardSprites;//读取图集中所有切片
private string fileName = "2048";//图集的名字
public int _currentIndex = 0;//当前卡片的显示编号
void Awake()
{
CardSprites = Resources.LoadAll<Sprite>(fileName);
}
//根据卡片编号修改SpriteRendere,从而改变卡片的数值
public void Generate(int index)
{
_currentIndex = index;
GetComponent<SpriteRenderer>().sprite = CardSprites[_currentIndex];
}
//合并卡片的逻辑
public void Merge()
{
_currentIndex++;
GetComponent<SpriteRenderer>().sprite = CardSprites[_currentIndex];
}
}
(3)接下来修改GameManager.cs脚本,双击打开脚本,修改代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject card;//卡片对象
private GameObject[,] cardList = new GameObject[4, 4];//卡片游戏对象对应的棋盘格子
private int CardNum = 0;//棋盘格子的卡片计数,用于满格后重新开始游戏
void Start()
{
CreateBG();
CreateCard();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.W))//上
{
MoveUp();
CreateCard();
}
if (Input.GetKeyDown(KeyCode.S))//下
{
MoveUp();
MoveDown();
}
if (Input.GetKeyDown(KeyCode.A))//左
{
MoveLeft();
CreateCard();
}
if (Input.GetKeyDown(KeyCode.D))//右
{
MoveRight();
CreateCard();
}
}
void CreateBG()
{
GameObject BG = new GameObject("BG");//创建空游戏对象作为背景预制体
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
Vector2 newPos = new Vector2(BeginPos.x + j * OffsetX, BeginPos.y - i * OffsetY);
Instantiate(bgSprite, newPos, Quaternion.identity, BG.transform);
}
}
}
void CreateCard()
{
}
void MoveUp()
{
}
void MoveDown()
{
}
void MoveLeft()
{
}
void MoveRight()
{
}
}
CreateCard函数的主要实现思路是:
void CreateCard()
{
CardNum = 0;
foreach (var item in cardList)
{
if (item)
{
CardNum++;
}
}
if (CardNum >= 16)
{
ResetGame();
return;
}
int X_index, Y_index = 0;
do
{
X_index = Random.Range(0, 4);
Y_index = Random.Range(0, 4);
} while (cardList[X_index, Y_index]);
Vector2 newPos = GetPosVector2(X_index, Y_index);
cardList[X_index, Y_index] = Instantiate(_card, newPos, Quaternion.identity);
if (Random.Range(0.0f,1.0f)>0.5f)
{
cardList[X_index, Y_index].GetComponent<Card>().Generate(1);
}
else
{
cardList[X_index, Y_index].GetComponent<Card>().Generate(2);
}
}
public Vector2 GetPosVector2(int x, int y)
{
return new Vector2(BeginPos.x + y * OffsetX, BeginPos.y - x * OffsetY);
}
void ResetGame()
{
foreach (var card in cardList)
{
if (card != null)
{
Destroy(card);
}
cardList = new GameObject[4, 4];
}
}
(3)接下来就是响应鼠标输入,然后移动卡片了,以向上移动为例,主要逻辑思路如下:
向上移动代码实现如下:
void MoveUp()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
x--;//根据方向的不同 x--是向上 x++是向下
if (x < 0 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (x >= 0 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x + 1, y] = temp;
cardList[x + 1, y].transform.position = GetPosVector2(x + 1, y);
}
}
}
}
}
}
}
向下移动:
void MoveDown()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
x++;//根据方向的不同 x--是向上 x++是向下
if (x > 3 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (x <= 3 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x - 1, y] = temp;
cardList[x - 1, y].transform.position = GetPosVector2(x - 1, y);
}
}
}
}
}
}
}
向左移动:
void MoveLeft()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
y--;//根据方向的不同 x--是向上 x++是向下
if (y < 0 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (y >= 0 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x, y + 1] = temp;
cardList[x, y + 1].transform.position = GetPosVector2(x, y + 1);
}
}
}
}
}
}
}
向右移动:
void MoveRight()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
y++;//根据方向的不同 x--是向上 x++是向下
if (y > 3 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (y <= 3 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x, y - 1] = temp;
cardList[x, y - 1].transform.position = GetPosVector2(x, y - 1);
}
}
}
}
}
}
}
运行游戏,就可以愉快的玩耍了:
到这一步,我们就完成了《2048》游戏的核心代码实现,但是我们的代码只有满格后重新开始游戏的判断,没有游戏完成的判断,下面加一个游戏成功的代码,继续修改GameManager.cs脚本:
void CreateCard()
{
CardNum = 0;
foreach (var item in cardList)
{
if (item)
{
CardNum++;
}
}
if (CardNum >= 16)
{
ResetGame();
return;
}
int X_index, Y_index = 0;
do
{
X_index = Random.Range(0, 4);
Y_index = Random.Range(0, 4);
} while (cardList[X_index, Y_index]);
Vector2 newPos = GetPosVector2(X_index, Y_index);
cardList[X_index, Y_index] = Instantiate(_card, newPos, Quaternion.identity);
if (Random.Range(0.0f,1.0f)>0.5f)
{
cardList[X_index, Y_index].GetComponent<Card>().Generate(1);
}
else
{
cardList[X_index, Y_index].GetComponent<Card>().Generate(2);
}
SucceedGame();
}
void SucceedGame()
{
foreach (var item in cardList)
{
if (item.GetComponent<Card>()._currentIndex >= 11)
{
Debug.Log("游戏成功");
break;
}
}
ResetGame();
}
整体GameManager.cs脚本代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject bgSprite;//背景卡片
private Vector2 BeginPos = new Vector2(-1.5f, 1.5f);//差不多在屏幕中间
private float OffsetX = 1.1f;//xy 加个0.1 有个间隙
private float OffsetY = 1.1f;
public GameObject _card;//卡片对象
private GameObject[,] cardList = new GameObject[4, 4];//卡片游戏对象对应的棋盘格子
private int CardNum = 0;//棋盘格子的卡片计数,用于满格后重新开始游戏
void Start()
{
CreateBG();
CreateCard();
CreateCard();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.W))//上
{
MoveUp();
CreateCard();
}
if (Input.GetKeyDown(KeyCode.S))//下
{
MoveDown();
CreateCard();
}
if (Input.GetKeyDown(KeyCode.A))//左
{
MoveLeft();
CreateCard();
}
if (Input.GetKeyDown(KeyCode.D))//右
{
MoveRight();
CreateCard();
}
}
void CreateBG()
{
GameObject BG = new GameObject("BG");//创建空游戏对象作为背景预制体
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
Vector2 newPos = new Vector2(BeginPos.x + j * OffsetX, BeginPos.y - i * OffsetY);
Instantiate(bgSprite, newPos, Quaternion.identity, BG.transform);
}
}
}
void CreateCard()
{
CardNum = 0;
foreach (var item in cardList)
{
if (item)
{
CardNum++;
}
}
if (CardNum >= 16)
{
ResetGame();
return;
}
int X_index, Y_index = 0;
do
{
X_index = Random.Range(0, 4);
Y_index = Random.Range(0, 4);
} while (cardList[X_index, Y_index]);
Vector2 newPos = GetPosVector2(X_index, Y_index);
cardList[X_index, Y_index] = Instantiate(_card, newPos, Quaternion.identity);
if (Random.Range(0.0f,1.0f)>0.5f)
{
cardList[X_index, Y_index].GetComponent<Card>().Generate(1);
}
else
{
cardList[X_index, Y_index].GetComponent<Card>().Generate(2);
}
SucceedGame();
}
public Vector2 GetPosVector2(int x, int y)
{
return new Vector2(BeginPos.x + y * OffsetX, BeginPos.y - x * OffsetY);
}
void ResetGame()
{
foreach (var item in cardList)
{
if (item != null)
{
Destroy(item);
}
cardList = new GameObject[4, 4];
}
}
void SucceedGame()
{
foreach (var item in cardList)
{
if (item.GetComponent<Card>()._currentIndex >= 11)
{
Debug.Log("游戏成功");
break;
}
}
ResetGame();
}
void MoveUp()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
x--;//根据方向的不同 x--是向上 x++是向下
if (x < 0 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (x >= 0 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x + 1, y] = temp;
cardList[x + 1, y].transform.position = GetPosVector2(x + 1, y);
}
}
}
}
}
}
}
void MoveDown()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
x++;//根据方向的不同 x--是向上 x++是向下
if (x > 3 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (x <= 3 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x - 1, y] = temp;
cardList[x - 1, y].transform.position = GetPosVector2(x - 1, y);
}
}
}
}
}
}
}
void MoveLeft()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
y--;//根据方向的不同 x--是向上 x++是向下
if (y < 0 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (y >= 0 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x, y + 1] = temp;
cardList[x, y + 1].transform.position = GetPosVector2(x, y + 1);
}
}
}
}
}
}
}
void MoveRight()
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (cardList[i, j] != null)//当找到其中的卡片后
{
GameObject temp = cardList[i, j];//保留该卡片的引用
int x = i;
int y = j;
bool isFind = false;//设置查找标识
while (!isFind)
{
y++;//根据方向的不同 x--是向上 x++是向下
if (y > 3 || cardList[x, y])//达到边界或找到卡片后
{
isFind = true;
//判断值是否相同,相同的话合并操作
if (y <= 3 && cardList[x, y].GetComponent<Card>()._currentIndex == cardList[i, j].GetComponent<Card>()._currentIndex)
{
cardList[x, y].GetComponent<Card>().Merge();
Destroy(cardList[i, j]);
cardList[i, j] = null;
}
else//否则移动即可
{
cardList[i, j] = null;
cardList[x, y - 1] = temp;
cardList[x, y - 1].transform.position = GetPosVector2(x, y - 1);
}
}
}
}
}
}
}
}
至此,我们的《2048》游戏就已经完成了,愉快的玩耍吧。
https://download.csdn.net/download/q764424567/85013848
这篇文章,讲解了制作游戏的技术栈,以及推荐的学习书籍。
并且介绍了游戏创作大赛的比赛规则,在最后实现了《2048》游戏。
总体来说,实现这个游戏的难度不大,主要是对数组的操作,以及对于SpriteRenderer组件切换sprite的操作。
所以对于游戏开发不要觉得太难就停滞不前,试着去做,只有在路上,才会距离目标越来越近。
你的点赞就是对博主的支持,有问题记得留言:
博主还有跟多宝藏文章等待你的发掘哦:
专栏 | 方向 | 简介 |
---|---|---|
Unity3D开发小游戏 | 小游戏开发教程 | 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。 |
Unity3D从入门到进阶 | 入门 | 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。 |
Unity3D之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |
文章浏览阅读331次。第一部分:准备工作1 安装虚拟机2 安装centos73 安装JDK以上三步是准备工作,至此已经完成一台已安装JDK的主机第二部分:准备3台虚拟机以下所有工作最好都在root权限下操作1 克隆上面已经有一台虚拟机了,现在对master进行克隆,克隆出另外2台子机;1.1 进行克隆21.2 下一步1.3 下一步1.4 下一步1.5 根据子机需要,命名和安装路径1.6 ..._创建一个hadoop项目
文章浏览阅读1.7k次。心脏滴血漏洞HeartBleed CVE-2014-0160 是由heartbeat功能引入的,本文从深入码层面的分析该漏洞产生的原因_heartbleed代码分析
文章浏览阅读1.4k次。前言ofd是国家文档标准,其对标的文档格式是pdf。ofd文档是容器格式文件,ofd其实就是压缩包。将ofd文件后缀改为.zip,解压后可看到文件包含的内容。ofd文件分析工具下载:点我下载。ofd文件解压后,可以看到如下内容: 对于xml文件,可以用文本工具查看。但是对于印章文件(Seal.esl)、签名文件(SignedValue.dat)就无法查看其内容了。本人开发一款ofd内容查看器,..._signedvalue.dat
文章浏览阅读1.8w次,点赞29次,收藏313次。整体系统设计本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。其整体系统框图如下:图1:FPGA数据采集系统框图从图中可以看出,该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、D.._基于fpga的信息采集
文章浏览阅读2.5w次。1.背景错误信息:-- [http-nio-9904-exec-5] o.s.c.n.z.filters.post.SendErrorFilter : Error during filteringcom.netflix.zuul.exception.ZuulException: Forwarding error at org.springframework.cloud..._com.netflix.zuul.exception.zuulexception
文章浏览阅读358次。1.介绍图的相关概念 图是由顶点的有穷非空集和一个描述顶点之间关系-边(或者弧)的集合组成。通常,图中的数据元素被称为顶点,顶点间的关系用边表示,图通常用字母G表示,图的顶点通常用字母V表示,所以图可以定义为: G=(V,E)其中,V(G)是图中顶点的有穷非空集合,E(G)是V(G)中顶点的边的有穷集合1.1 无向图:图中任意两个顶点构成的边是没有方向的1.2 有向图:图中..._给定一个邻接矩阵未必能够造出一个图
文章浏览阅读321次。(十二)、WDS服务器安装通过前面的测试我们会发现,每次安装的时候需要加域光盘映像,这是一个比较麻烦的事情,试想一个上万个的公司,你天天带着一个光盘与光驱去给别人装系统,这将是一个多么痛苦的事情啊,有什么方法可以解决这个问题了?答案是肯定的,下面我们就来简单说一下。WDS服务器,它是Windows自带的一个免费的基于系统本身角色的一个功能,它主要提供一种简单、安全的通过网络快速、远程将Window..._doc server2012上通过wds+mdt无人值守部署win11系统.doc
文章浏览阅读219次。python–xlrd/xlwt/xlutilsxlrd只能读取,不能改,支持 xlsx和xls 格式xlwt只能改,不能读xlwt只能保存为.xls格式xlutils能将xlrd.Book转为xlwt.Workbook,从而得以在现有xls的基础上修改数据,并创建一个新的xls,实现修改xlrd打开文件import xlrdexcel=xlrd.open_workbook('E:/test.xlsx') 返回值为xlrd.book.Book对象,不能修改获取sheett_xlutils模块可以读xlsx吗
文章浏览阅读8.2w次,点赞267次,收藏656次。运行Selenium出现'WebDriver' object has no attribute 'find_element_by_id'或AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'等定位元素代码错误,是因为selenium更新到了新的版本,以前的一些语法经过改动。..............._unresolved attribute reference 'find_element_by_id' for class 'webdriver
文章浏览阅读198次。一:模态窗口//父页面JSwindow.showModalDialog(ifrmehref, window, 'dialogWidth:550px;dialogHeight:150px;help:no;resizable:no;status:no');//子页面获取父页面DOM对象//window.showModalDialog的DOM对象var v=parentWin..._jquery获取父window下的dom对象
文章浏览阅读1.7w次,点赞15次,收藏129次。算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵 魂。二、算法的特征1.可行性 算法中执行的任何计算步骤都可以分解为基本可执行的操作步,即每个计算步都可以在有限时间里完成(也称之为有效性) 算法的每一步都要有确切的意义,不能有二义性。例如“增加x的值”,并没有说增加多少,计算机就无法执行明确的运算。 _算法
文章浏览阅读1.5k次,点赞18次,收藏26次。网络安全的标准和规范是网络安全领域的重要组成部分。它们为网络安全提供了技术依据,规定了网络安全的技术要求和操作方式,帮助我们构建安全的网络环境。下面,我们将详细介绍一些主要的网络安全标准和规范,以及它们在实际操作中的应用。_网络安全标准规范