放假在家没事可做,游戏荒ing,做个商城教学,针对有一定基础的新手,先分析下商城购买装备是怎么实现的
购买流程
- 查询改装备的价格
- 查询当前玩家有多少钱
- 判断钱够不够买装备
- 如果够:召唤师的钱-=装备的钱
- 查询现在玩家有什么装备
- 给玩家添加改装备
- 查询该装备的属性加成
- 查询玩家属性值
- 更新玩家属性值:英雄属性+=装备属性加成
先看下最终效果图
我们一步一步的来,先从最简单的开始
一、准备工作
随便搭个简单的商城界面,配置下sql数据像这样
二、封装数据库(SqlBase)
之后另开一篇来讲sql框架是怎么封装的,先列几个用到的api。
ExecuteNonQuery:如果您不希望返回任何结果集,请使用此操作在SQL Server中执行任意SQL语句。
ExecuteReader:如果希望将结果集作为DataSet数组返回(如果有的话),请使用此操作在SQL Server中执行任意SQL语句。
ExecuteScalar:使用此操作可以执行SQL Server中的任何任意SQL语句以返回单个值。此操作仅在SQL语句返回的结果集中第一行的第一列中返回值。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mono.Data.Sqlite;
public class SqlBase
{
//单例
private static SqlBase Instance;
public static SqlBase GetInstance()
{
if (Instance == null)
{
Instance = new SqlBase();
}
return Instance;
}
private SqliteConnection con;
private SqliteCommand command;
private SqliteDataReader reader;
/// <summary>
/// 打开数据库
/// </summary>
/// <param name="dbName">文件名</param>
public void OpenSqlData(string dbName)
{
if (!dbName.EndsWith(".sqlite"))
{
dbName += ".sqlite";
}
string path = "";
#if UNITY_EDITOR || UNITY_STANDALONE
path = "Data Source = "
+ Application.streamingAssetsPath
+ "/" + dbName;
#endif
//实例化连接对象
con = new SqliteConnection(path);
//打开连接
con.Open();
//实例化指令对象
command = con.CreateCommand();
}
public void CloseSqlData()
{
con.Close();
command.Dispose();
con = null;
command = null;
}
/// <summary>
/// 执行语句
/// </summary>
/// <param name="sqlQuery"></param>
/// <returns></returns>
public int JustExecute(string sqlQuery)
{
command.CommandText = sqlQuery;
return command.ExecuteNonQuery();
}
/// <summary>
/// 查询单个语句
/// </summary>
/// <param name="sqlQuery"></param>
/// <returns></returns>
public object SeletSingleData(string sqlQuery)
{
command.CommandText = sqlQuery;
return command.ExecuteScalar();
}
/// <summary>
/// 查询多种数据
/// </summary>
/// <param name="sqlQuery"></param>
/// <returns></returns>
public List<ArrayList> SeletMultipleData(string sqlQuery)
{
command.CommandText = sqlQuery;
reader = command.ExecuteReader();
//存储数据 List<ArrayList>==>List<List<Object>>
List<ArrayList> result = new List<ArrayList>();
while (reader.Read())
{
//新建ArrayList存储当行的数据
ArrayList rowData = new ArrayList();
//遍历所有的列
for (int i = 0; i < reader.FieldCount; i++)
{
//将当前行的当前列添加到rowData
rowData.Add(reader.GetValue(i));
}
//将储存好的当前行数据添加到List
result.Add(rowData);
}
reader.Close();
return result;
}
}
三、进行二次封装
根据购买流程的分析,我们需要在数据库中查询到:
英雄:英雄金币、英雄属性
商店:装备列表、装备价格、装备属性
主要进行的操作:
对于英雄:花费金钱,英雄属性变化(买装备)
封装功能:
- 查询英雄名称
- 查询英雄有多少钱
- 查询英雄属性
- 查询英雄装备列表
- 查询商城中所有装备
- 查询装备属性信息
- 设置玩家金钱
- 花费玩家金钱
- 给玩家添加装备给英雄添加装备属性
- 买装备
之后在进行一系列操作,对于商城数据库的封装做到这些功能就ok了
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShopSqlFrameWork : SqlBase
{
//单例方便调用
private static ShopSqlFrameWork Instance;
public static ShopSqlFrameWork GetInstance()
{
if (Instance == null)
{
Instance = new ShopSqlFrameWork();
}
return Instance;
}
//sql语句
private string sqlQuery;
public delegate void CallBack();
/// <summary>
/// 查询英雄有多少钱
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public int SeletHeroMoney(string heroName)
{
sqlQuery = string.Format("Select PlayerMoney From PlayerInfo Where HeroName='{0}'", heroName);
object money = SeletSingleData(sqlQuery);
if (money==null)
{
return -1;
}
return Convert.ToInt32(money);
}
/// <summary>
/// 查询英雄属性
/// </summary>
/// <param name="heroName"></param>
/// <returns></returns>
public int[] SeletHeroProperties(string heroName)
{
sqlQuery =string .Format("Select * From PlayerInfo Where HeroName = '{0}'",heroName);
List<ArrayList> properties = SeletMultipleData(sqlQuery);
//定义长度是因为自己知道,其他功能要令处理
int[] result = new int[2];
if (properties.Count!=0)
{
for (int i = 1; i <= 2 ; i++)
{
result[i - 1] = Convert.ToInt32(properties[0][i]);
}
return result;
}
return null;
}
/// <summary>
/// 查询英雄名称
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public string SeletHeroName(int index)
{
sqlQuery = "Select HeroName From PlayerInfo";
List<ArrayList> heros = SeletMultipleData(sqlQuery);
return heros[index][0].ToString();
}
/// <summary>
/// 查询英雄装备列表
/// </summary>
/// <param name="heroName"></param>
/// <param name="call"></param>
/// <returns></returns>
public string SelectHeroEquips(string heroName, CallBack call)
{
sqlQuery =string .Format("Select PlayerEquips From PlayerInfo Where HeroName='{0}'",heroName);
object result = SeletSingleData(sqlQuery);
if (result == null)
{
call();
// Debug.Log("没有改英雄!");
return null;
}
return result.ToString();
}
/// <summary>
/// 查询商城所有装备 方便加载
/// </summary>
/// <returns></returns>
public string [] SeletAllShopEquips()
{
sqlQuery= "Select * From ShopData";
List<ArrayList> result = SeletMultipleData(sqlQuery);
string[] equipNames = new string[result.Count];
for (int i = 0; i < result.Count; i++)
{
//打印所有sql表中shopdata第一列装备名字
equipNames[i] = result[i][0].ToString();
}
return equipNames;
}
/// <summary>
/// 查询装备价格
/// </summary>
/// <param name="equipName"></param>
/// <returns></returns>
private int SelectEquipPrice(string equipName,CallBack call)
{
sqlQuery =string .Format("Select EquipMoney From ShopData Where EquipName='{0}'",equipName);
object price = SeletSingleData(sqlQuery);
if (price ==null)
{
call();
//Debug.Log("没有改装备");
}
return Convert.ToInt32(price);
}
/// <summary>
/// 查询装备属性信息
/// </summary>
/// <param name="equipName"></param>
/// <returns></returns>
private int[] SeletEquipProperties(string equipName)
{
sqlQuery = string.Format("Select * From ShopData Where EquipName = '{0}'", equipName);
List<ArrayList> properties = SeletMultipleData(sqlQuery);
int[] result = new int[2];
if (properties.Count!=null)
{
for (int i = 1; i <=2; i++)
{
result[i - 1] = Convert.ToInt32(properties[0][i]);
}
return result;
}
return null;
}
/// <summary>
/// 设置玩家金钱
/// </summary>
/// <param name="heroName"></param>
/// <param name="newMoney"></param>
private void SetHeroMoney(string heroName,int newMoney)
{
sqlQuery = string.Format("Update PlayerInfo Set PlayerMoney={0} Where HeroName='{1}'", newMoney, heroName);
JustExecute(sqlQuery);
}
/// <summary>
///判断玩家是否能花 花费玩家金钱
/// </summary>
/// <param name="heroName"></param>
/// <param name="cost"></param>
/// <returns></returns>
private bool CostHeroMoney(string heroName,int cost)
{
int heroMoney = SeletHeroMoney(heroName);
if (heroMoney>=cost)
{
SetHeroMoney(heroName, heroMoney - cost);
return true;
}
return false;
}
/// <summary>
/// 给英雄添加装备
/// </summary>
/// <param name="heroName"></param>
/// <param name="equipName"></param>
public void AddHeroEquip(string heroName,string equipName)
{
//查询英雄装备
string heroEquips = SelectHeroEquips(heroName,null);
if (heroEquips!=null)
{
heroEquips += equipName + "|";
}
//跟信道数据库中
sqlQuery = string.Format("Update PlayerInfo Set PlayerEquips='{0}'", heroEquips);
JustExecute(sqlQuery);
}
/// <summary>
/// 给英雄添加一个属性
/// </summary>
/// <param name="heroName"></param>
/// <param name="equipName"></param>
private void AddHeroEquipProperties(string heroName,string equipName)
{
//获取英雄属性
int[] heroProperties = SeletHeroProperties(heroName);
//获取装备属性
int[] equipProperties = SeletEquipProperties(equipName);
//给英雄加buff
for (int i = 0; i < 2; i++)
{
heroProperties[i] += equipProperties[i];
}
//更新英雄数据
sqlQuery = string.Format("Update PlayerInfo Set SpellPower = {0}," +
"DefenceValue={1} Where HeroName='{2}'", heroProperties[0], heroProperties[1], heroName);
JustExecute(sqlQuery);
}
public void BuyEquip(string heroName,string equipName, CallBack call)
{
int price = SelectEquipPrice(equipName,null);
if (!CostHeroMoney(heroName,price))
{
call();
return;
}
AddHeroEquipProperties(heroName, equipName);
AddHeroEquip(heroName, equipName);
}
}
四、设置商城图片,背包图片
新建两个脚本ShopEquip,BagEquip
新建两个Image设置好大小之后,分别添加ShopEquip(另外添加Button组件),BagEquip。
建一个Resources和Textures文件夹,像这样
using UnityEngine;
using UnityEngine.UI;
public class BagEquip : MonoBehaviour
{
private Image _image;
/// <summary>
/// 设置装备的图片
/// </summary>
/// <param name="equipName"></param>
public void SetEquipSprite(string equipName)
{
//找到Image
_image = GetComponent<Image>();
//动态加载图片
_image.sprite = Resources.Load<Sprite>("Textures/" + equipName);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ShopEquip : MonoBehaviour
{
private Image _image;
private Button _button;
/// <summary>
/// 设置装备图片
/// </summary>
/// <param name="equiName"></param>
public void SetEquipSprite(string equipName)
{
_image = GetComponent<Image>();
_button = GetComponent<Button>();
_image.sprite = Resources.Load<Sprite>("Textures/" + equipName);
_button.onClick.AddListener(() =>
{
//买装备的数据库操作
ShopSqlFrameWork.GetInstance().BuyEquip(
ShopView.Instance.heroName,
_image.sprite.name, NoMoney);
//更新数据库内容到界面
ShopView.Instance.UpdateView();
});
}
/// <summary>
/// 没钱了 在这里进行一些弹出界面等操作
/// </summary>
private void NoMoney()
{
Debug.Log("兄弟没钱了");
}
}
五、UI显示设置更新数据
代码并不难,后面的代码比较乱 做了注释
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ShopView : MonoBehaviour
{
public static ShopView Instance;
[Header("召唤师名称")]
public Text heroNameText;
[Header("英雄属性")]
public Text[] heroPropertiesText;
[Header("召唤师金钱")]
public Text heroMoneyText;
[Header("背包")]
public Transform bagTra;
[Header("商城")]
public Transform shopTra;
public string heroName;
private ShopSqlFrameWork ShopSql;
private GameObject bagEquipPrefab;
private GameObject shopEquipPrefab;
private void Awake()
{
Instance = this;
ShopSql = ShopSqlFrameWork.GetInstance();
}
private void Start()
{
bagEquipPrefab = GetRes("Prefabs/BagEquip");
shopEquipPrefab = GetRes("Prefabs/ShopEquip");
ShopSql.OpenSqlData("ShopData");
ShowShopEquips();
UpdateView();
}
//程序退出的时候 关闭数据库
private void OnApplicationQuit()
{
ShopSql.CloseSqlData();
}
private GameObject GetRes(string path)
{
return Resources.Load<GameObject>(path);
}
/// <summary>
/// 加载商店里的所有装备
/// </summary>
private void ShowShopEquips()
{
string[] names = ShopSql.SeletAllShopEquips();
for (int i = 0; i < names.Length; i++)
{
GameObject resEquips = Instantiate(shopEquipPrefab);
resEquips.transform.SetParent(shopTra.GetChild(i));
resEquips.transform.localPosition = Vector3.zero;
resEquips.transform.localPosition = Vector3.zero;
resEquips.GetComponent<ShopEquip>().SetEquipSprite(names[i]);
}
}
/// <summary>
/// 更新界面
/// </summary>
public void UpdateView()
{
//获取英雄名称
heroName = ShopSql.SeletHeroName(0);
//显示名称
heroNameText.text = heroName;
//获取英雄属性
int[] heroProperties = ShopSql.SeletHeroProperties(heroName);
//显示英雄属性
for (int i = 0; i <2; i++)
{
heroPropertiesText[i].text = heroProperties[i].ToString();
}
//获取金钱
int heroMoney = ShopSql.SeletHeroMoney(heroName);
//显示金钱
heroMoneyText.text = heroMoney.ToString();
//获取英雄装备列表
string equips = ShopSql.SelectHeroEquips(heroName,null);
//清空背包的所有装备
for (int i = 0; i < bagTra.childCount - 1; i++)
{
if (bagTra.GetChild(i).childCount > 0)
{
//销毁原来的装备图片
Destroy(bagTra.GetChild(i).GetChild(0).gameObject);
}
}
//获取每个装备的名称
string[] equipNames = equips.Split('|');
for (int i = 0; i < equipNames.Length; i++)
{
//如果装备名称不正常,跳过
if (string.IsNullOrEmpty(equipNames[i]))
continue;
//生成背包装备
GameObject crtEquip = Instantiate(bagEquipPrefab);
//设置父物体
crtEquip.transform.SetParent(bagTra.GetChild(i));
//设置相对坐标
crtEquip.transform.localPosition = Vector3.zero;
//设置当前装备的图片
crtEquip.GetComponent<BagEquip>().SetEquipSprite(equipNames[i]);
}
}
}
好啦大体功能就实现了 ,之后有时间继续完善,比如卖装备,装备限制等功能。