说明
本文为泰课在线免费项目Flappy Bird的学习笔记,这个系统化的整理知识,对我来说很有用,所以我记录了下来。
- 整理知识,学习笔记
- 练习Markdown的书写方法
- 可以和大家分享学习方法,互相进步,嘿嘿
教学视频跳转
正文
一、提取安卓安装包中的资源并处理
下载安装包之后修改后缀为zip就可以提取
gfx
中的图集和res中的文本资源文件
需要写个小程序来根据文本处理图片,如下
Visual Studio制作图集解析工具
- 1.新建项目选择windows窗体应用程序---AtlasTool,vs会自动创建子文件夹,一个图片控件---PictureBox,按钮---Button。
- 2.去掉放大按钮,和去掉拖拽功能,窗体属性中设置AutoSizeMode-GrowAndShrink这个是拖拽,MaximizeBox-False去掉放大,Text改名。
- 3.PictureBox改名,picImage-组件加功能,Button改名,btnParse,text=解析。
4.设计过程
SpriteInfo-小图的信息,配置文件中一行信息,放回给Parser中数组
Parser-解析配置信息,保持数据
Loader-加载图集和配置文件
Cutter-裁剪,给它大图,spriteInfo中配置,开始裁剪
AtlasFacade-主角,
- 5.编写
设置拖拽事件,行为-allowdrop-true,设置事件,设置图片控件sizeMode-StretchImage。
//AtlasTool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AtlasTool
{
class SpriteInfo
{
public string Name { get; private set; }
public int X{ get; private set; }
public int Y { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public SpriteInfo(string name, int x, int y, int width, int heigt)
{
this.Name = name;
this.X = x;
this.Y = y;
this.Width = width;
this.Height = heigt;
}
}
}
//AtlasTool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AtlasTool
{
class Parser
{
public SpriteInfo[] SpriteInfos { get; private set; }
public void Parse(int width,int height, string config)
{
List list = new List();
string[] lines= config.Split(new char[] { '\n' } ,StringSplitOptions.RemoveEmptyEntries);
foreach (string line in lines)
{
//button_play 116 70 0.6855469 0.22851562 0.11328125 0.068359375
string[] parts= line.Split(new char[] { ' ' });
SpriteInfo info = new SpriteInfo(
parts[0],
(int)(Convert.ToSingle(parts[3])* width),
(int)(Convert.ToSingle(parts[4]) * height),
Convert.ToInt32(parts[1]),
Convert.ToInt32(parts[2])
);
list.Add(info);
}
SpriteInfos = list.ToArray();
}
}
}
//AtlasFacade
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
namespace AtlasTool
{
class AtlasFacade
{
Loader m_Loader=null;
Parser m_Parser=null;
Cutter m_Cutter=null;
public Bitmap[] Images { get; private set; }
public AtlasFacade()
{
m_Loader = new Loader();
m_Parser = new Parser();
m_Cutter = new Cutter();
}
//验证
public bool Valid(string imageFile)
{
return imageFile.ToLower().EndsWith(".png");
}
//处理
public void Process(string imageFile)
{
//加载
m_Loader.Load(imageFile);
//大图
Bitmap sourceImage = m_Loader.Image;
//配置
string config = m_Loader.Config;
//-------解析
m_Parser.Parse(sourceImage.Width, sourceImage.Height, config);
SpriteInfo[] spriteInfos = m_Parser.SpriteInfos;
//-------切割
m_Cutter.Cut(sourceImage, spriteInfos);
Images = m_Cutter.Images;
}
//保持
public void Save(string path=null)
{
//默认用原来的路径目录
if (path == null)
{
path = m_Loader.ResourceDir;
}
for (int i = 0; i < m_Parser.SpriteInfos.Length; i++)
{
SpriteInfo info = m_Parser.SpriteInfos[i];
Bitmap image = Images[i];
string fileName = path + "\\" + info.Name+".png";
image.Save(fileName,ImageFormat.Png);
}
}
}
}
//Cutter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
namespace AtlasTool
{
class Cutter
{
public Bitmap[] Images { get; private set; }
public void Cut(Bitmap sourceImage, SpriteInfo[] spriteInfos)
{
List list = new List();
foreach (SpriteInfo info in spriteInfos)
{
Bitmap smallImage = new Bitmap(info.Width, info.Height);
Graphics g = Graphics.FromImage(smallImage);
g.DrawImage(
sourceImage,
new Rectangle(0, 0, info.Width, info.Height),
new Rectangle(info.X, info.Y, info.Width, info.Height),
GraphicsUnit.Pixel);
g.Dispose();
list.Add(smallImage);
}
Images = list.ToArray();
}
}
}
//AtlasTool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
namespace AtlasTool
{
class Loader
{
public Bitmap Image { get; private set; }
public string Config { get; private set; }
public string ResourceDir { get; private set; }
public void Load(string imageFile)
{
//C:\Users\fjqmt\Desktop\Flappy Bird\Atlas\atlas.png
//C:\Users\fjqmt\Desktop\Flappy Bird\Atlas
ResourceDir = Path.GetDirectoryName(imageFile);
Image = new Bitmap(imageFile);
//C:\Users\fjqmt\Desktop\Flappy Bird\Atlas\atlas.png
//atlas
string fileName = Path.GetFileNameWithoutExtension(imageFile);
string configFile = ResourceDir + "\\" + fileName + ".txt";
StreamReader sr = new StreamReader(configFile);
Config = sr.ReadToEnd();
sr.Close();
sr.Dispose();
}
}
}
//AtlasTool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AtlasTool
{
static class Program
{
///
/// 应用程序的主入口点。
///
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AtlasTool
{
public partial class Form1 : Form
{
AtlasFacade m_Facade = new AtlasFacade();
string m_File = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnParse_Click(object sender, EventArgs e)
{
if (m_File == null)
{
MessageBox.Show("请拖入一张图片进行处理!");
return;
}
bool isSuccess = true;
try
{
//处理
m_Facade.Process(m_File);
//结束
m_Facade.Save();
}
catch (Exception ex)
{
isSuccess = false;
MessageBox.Show("解析失败!" + ex.Message);
}
if (isSuccess)
{
MessageBox.Show("解析成功!");
}
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
//判断
string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
string file = files[0];
if (!m_Facade.Valid(file))
{
MessageBox.Show("需要图片文件");
return;
}
//保存
m_File = file;
//显示
picImage.Load(file);
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Move;
}
else
{
e.Effect = DragDropEffects.None;
}
}
}
}
二、Unity
1.流程和设计图
2.操作流程
Game-挂摄像机上,GameUI挂ui上,游戏的状态-枚举,Global全局定义枚举enum GameState.
UI通过swith选择枚举状态来改变。
Game-导演不要有太多逻辑
单例
private static Game m_Instance=null;
public static Game Instance{get{return m_Instance;}}
void Awake(){m_Instance=this;}
类似于new
定义一个事件
public event Action OnStateChanged
变量
GameState m_State=GameState.Init;
重构---封装字段
public GameState GameState
{
get{return m_State;}
private set{
set 的时候事件不为空就触发事件
m_State=value;
if(OnStateChanged!=null)
OnStateChanged(m_State);
}
}
演员
public GameUI GameUI=null;
void Start()
{
OnStateChanged+=Game_ OnStateChanged;
初始进入Init
this.GameState=GameState.Init;
------------------先建立连接让事件与函数挂钩,再设置属性,触发set方法中事件,触发了事件绑定的函数就会触发,
}
void Game_ OnStateChanged(GameState state)
{
GameUI.UpdateUI(state);
}
Bird类
隐藏于显示
public bool IsVisible
{
get{return gameObject.activeInHierarchy;}
set{gameObject.SetActive(value);}
}
商店下载
先登陆,uinty中的右上角第三个,搜索框里头输入Itween,下一行选择商店,free免费,inp什么导入
iTween.MoveBy(
gameObject,
iTween.Hash(
"y",10,
"easeType",iTween.EaseType.linear,
"loopType",iTween.Looptype.pingPong,
"time",3f
)
)
2.场景和小鸟
图片数组
public Sprite[] Images;
随机显示
Random 在system和unity 中都有,如果用不到system就注释掉
public void RandomShow()
{
随机索引
int randomIndex=Random.Range(0,Images.Length);[0,10)如果加了f都可以取到
随机图片
Sprite image=Images[randomIndex];
获取组件
SpriteRenderer renderer=GetComponent();
设置图片
renderer.sprite=image;
}
输入控制
inputController
//是否触发Tab事件
public bool Enale{get;set;}
//点击事件 加event起到保护作用,外部不能触发
public event Action Ontab=null;
void Update()
{
if(Enale &&input.GetMouseButtonDown(0))
{
不为空的时候触发事件
if(OnTab!=null)
{
OnTab();
}
}
}
小鸟需要:跳跃的力,是否显示,是否受重力作用,事件---穿过管道----加分,事件---小鸟死掉
状态,活着,死亡,重新开始
rigidbody2d
播放动画不播放可以设置enabled=false;或者seep=0;
设置速度,获取刚体,.velocity=vector3.up*speed;速度值是一样的,加速度会与来越快
碰撞检测,那一层与那一层可以碰撞,physics里设置,Layers中的第一个是射线层什么的- -,
解决靠近两个无缝物体之间的缝隙问题,选择贴近物体,按v,鼠标移动到要靠近的顶点,会出现一个小框,拖动,靠近其他顶点。松手。
预设体,按等级分类放置
const 常量
transform是可以foreach 因为transform有继承ienumerable
child in transform if child.name=="",child.gameObject.SetActive
设置2d隐藏,某个组件
遍历子对象,foreach(Transform child in obstacle.transform)
{
if(name=="")
child.name和child.gameobject.name是一样的
还有gameobject
}
for(int i=0;i();
foreach(BoxCollider2D collider in colliders)
{
collider.enabled=enable;
}
if(onScoreChanged!=null)
onScoreChanged(m_Score);如果事件有人监听就触发
PlayerPrefs.GetInt();
private int m_Score=0;
public int Score
{
get{return m_Score;}
set{
m_Score=value;
if(onScoreChanged!=null)
onScoreChanged(m_Score);
}
}
public int Best
{
get{return PlayerPrefs.GetInt("Best",0)}
set{PlayerPrefs.SetInt("Best",value);}
}
void Saver_OnScoreChanged(int Score)
{
if(score>Best)
{
Best=score;
}
}
这样自己监听自己,如果发现改变分数大于最大值,就保存,Score的代码都和Score,Best也是,低耦合
声音集合AudioClip[]
找到之后break之后就不用再找了
AudioSource.PlayClipAtPoint(clip,transform.position)世界坐标
声音放在摄像机上,声音会大一些些
穿过就触发事件saver.score++,加加就触发事件更新ui的text,
重新命名ctrl+r
Author : Jhin.hx
Time : 2016.11.29