Unity游戏开发之2048游戏的实现
作者:恬静的小魔龙 发布时间:2023-04-19 00:11:33
一、前言
写今天这篇文章的缘由,其实是来自于前段时间和粉丝的一个聊天,最近他打算参加游戏创作大赛,问我需要准备学习什么知识,以及参加比赛的注意事项一类:
我相信因为热爱游戏而前来投身于U3D学习的粉丝想来不是少数,兴趣可以驱动学习,在完善自己心爱游戏的过程中,要不断的去学习,不断的提高自己。
而参与游戏设计比赛,更是提高自身技术实力、增长眼界见识的优秀途径,在此途中也能遇见不少志同道合的好朋友。
那今天就借此简单的聊一下,制作一个简单游戏需要具备哪些技术栈,以及参加这类比赛需要的知识基础。
并且,还有休闲类游戏《2048》的实战开发内容。
二、游戏开发知识储备
接下来就聊一下做一个游戏需要具备哪些技术栈, 因为只是面对“做出游戏”这个特定需求,所以有些技术没有列举出来,在开发中遇到可以再学习。
2-1技术栈
Unity3D安装
首先,学习Unity3D你要安装Unity3D编辑器吧,激活Unity Hub许可证。
分享 几个下载链接:
Unity3D中国版官网
Unity3D国际版官网
Unity3D下载链接
当然,比较推荐是用Unity Hub下载安装:
安装后激活许可证,这个网上教程比较多,这里就不赘述了:
Unity3D编辑器布局学习
学习Unity3D编辑器,要先熟悉Unity编辑器的布局,比如菜单栏,工具栏在什么位置:
Unity入门组件学习
Unity的特色就是面向组件编程,所以对于组件的熟悉是基本的要求。
比较常用的组件有:
Audio组
AudioSource
AudioListener
Effects组
ParticleSystem
LineRenderer
TrailRenderer
Event组
EventSystem
EventTrigger
GraphicRaycaster
Layout组
RectTransform
Canvas
CanvasGroup
CanvasScaler
AspectRatioFitter
ContentSizeFitter
GridLayoutGroup
HorizontalLayoutGroup
VerticleLayoutGroup
LayoutElement
Mesh组
MeshFilter
MeshRenderer
SkinnedMeshRenderer
TextMeshPro-Text
Miscellaneous组
Animator
Animation
Nevigation组
NavMeshAgent
NavMeshObstacle
Physics2D组
Rigidbody2D
BoxCollider2D
CapsuleCollider2D
CircleCollider2D
CompositeCollider2D
EdgeCollider2D
Physics组
Rigidbody
BoxCollider
CapsuleCollider
SphereCollider
CharacterController
TerrainCollider
Rendering组
Camera
CanvasRenderer
SpriteRenderer
Skybox
SortingGroup
Light
组件比较多,但是不需要全部学习,只需要在用到的时候查看一下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的基本组件:
Text
Image
RawImage
Button
Toggle
Slider
Scrollbar
Dropdown
InputField
Canvas
Panel
ScrollView
数据结构
数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素的集合。
简单点说就是专门用于数据存储和检索的类。
因为做游戏开发或者其他项目都会用到对数据的操作,而数据结构就是专门用于数据存储和检索的类。
常见的数据结构有:
列表List
数组Array
字典Dictionary
栈Stack
队列Queue
总结的技术栈看着字没有那么多,但是内容还是很多的,比如说Unity3D的组件、C#脚本语言、UGUI系统,每一块拿出来都是很多的内容。
三、休闲类游戏《2048》开发实战
3-1玩法概述
开局是一个4X4棋盘,棋盘中随机出现两个数字,出现的数字为2或4。
玩家通过选择上下左右四个反向,若玩家选择的方向上有相同的数字则合并,若选择的反向上没有相同的数字但是有空位则移动,可以移动的同时合并,但是不可以连续合并。
若棋盘被数字填满,无法进行移动或合并,则游戏失败。
如果棋盘出现2048,则游戏胜利:
3-2实现分析
根据规则,来分析一下游戏的实现。
以向左移动为例:
可以看到移动后的规则:
相邻的元素值相同则合并,然后末尾补上空元素
开头或中间有空元素就移动到末尾
只合并一次,不会多次合并
3-3搭建场景
(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对象删除,我们将在后面使用实例化新建出来。
至此,我们的场景就搭建完成了,下面进行代码的编写。
3-4实现代码
(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函数的主要实现思路是:
每次申城一个卡片时计算当前数组中的卡牌数,如果大于16则游戏重置
否则随机生成坐标点,判断当前当前坐标是否有卡片,直到找到空余位置,实例化卡片
生成对应数字的卡片
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];
}
}
(4)接下来就是响应鼠标输入,然后移动卡片了,以向上移动为例,主要逻辑思路如下:
遍历卡片数组,寻找存在的卡片
寻找到一个存在的卡片,沿着移动方向上每一个格子做判断,看前面是否有卡片,找到离自己最近的卡片做判断
如果与本身卡片数值一直,则进行Card类中的Merge方法进行合并
否则,将卡片移动到该格子的临近格子
向上移动代码实现如下:
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://blog.csdn.net/q764424567/article/details/123626745


猜你喜欢
- 在java 编程中,我们常常有这样的需求:需要将一段字符串内的特定字符串,按照一定规则查找出来或替换,比如匹配文本开头规则和结束规则。以下就
- 疑问都知道C#有装箱和拆箱的操作,听闻也都是讲int类型转换成object类型就是装箱,将object类型再转回int类型就是拆箱。描述的通
- 很多C#的教材都会强调对象相等的概念。我们都知道,在C#的世界里存在两种等同性。一种是逻辑等同性:如果两个对象在逻辑上代表同样的值,则称他们
- 目录1.问题引出2.解决办法3.另外一种自定义序列化机制(介绍Externalizable)1.问题引出在某些情况下,我们可能不想对于一个对
- 前言我们都知道在java中进行日期格式化使用simpledateformat。通过格式 yyyy-MM-dd 等来进行格式化,但是你知道其中
- 是不是很讨厌mybatis Generator帮我们生成代码的时候在Mapper和mapper.xml文件中生成的一大堆注解?今天在看MyB
- 指定相关的测试代码首先,写一个用于测试的关于Main(String[] args)参数输入有关的代码类,如下:using System;pu
- 服务端:using System;using System.Collections.Generic;using System.Net;usi
- 前言C#本身提供了很强大的控件库,但是很多控件库的功能只是一些基本的功能,就比如最简单的按钮,C#提供了最基础的按钮使用方法,但是如果要增加
- 一、概述无论是什么语言,在多线程编程中,常常会遇到多个线同时操作程某个变量(读/写),如果读/写不同步,则会造成不符合预期的结果。例如:线程
- protobuf 是 google的一个开源项目,可用于以下两种用途:(1)数据的存储(序列化和反序列化),类似于xml、json等;(2)
- 1、概述传统的Android开发架构一般是MVC模式,Model:业务逻辑和实体模型View:对应于布局文件Controllor:对应于Ac
- 1. 为什么使用线程池诸如 Web 服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小
- 可以用DSA和RSA,如: using System; using System.Text; using Sy
- 本文实例讲述了C#正则表达式匹配与替换字符串功能。分享给大家供大家参考,具体如下:事例一:\w+=>[A-Za-z1-9_],\s+=
- ViewDragHelper是support.v4下提供的用于处理拖拽滑动的辅助类,查看Android的DrawerLayout源码,可以发
- 在Word中可将文件通过OLE对象嵌入的方式插入到文档,包括Word、excel、PDF、PPT、图片、宏文件、文件包等在内的多种文件类型。
- 一:引入依赖包<!-- https://mvnrepository.com/artifact/org.springframework.
- API:即Application programming Interface,应用编程接口。Java中封装了许许多多的API供用户使用,Sc
- 1.问题引出源码: public static void main(String[] args) { List&l