精华内容
下载资源
问答
  • Android 3D游戏开发技术宝典,完整扫描版

    千次下载 热门讨论 2013-12-29 19:34:32
    《Android 3D游戏开发技术宝典:OpenGL ES 2.0》内容简介:随着智能手机移动嵌入式平台硬件性能的不断提升,3D游戏应用也逐渐普及开来。《Android 3D游戏开发技术宝典:OpenGL ES 2.0》结合作者多年从事3D游戏应用开发...
  • 3D游戏开发基础

    万人学习 2015-02-15 20:43:30
    该课程从游戏开发基础知识讲解,包括向量,矩阵,以及固定流水线,可编程流水线,GPU编程等,通过该课程的学习可以让大家对游戏开发原理以及游戏底层是如何运作逐一给大家讲解。
  • 这个工具呢,博主在Unity3D游戏开发之反编译AssetBundle提取游戏资源这篇文章中其实已经提到过了,不过因为有些朋友对如何使用这个工具依然存在问题,所以博主决定特地写一篇文章来讲解如何使用disunity来提取Unity...

    各位朋友,大家好,我是秦元培。今天博主想和分享的是使用disunity提取Unity3D游戏素材。这个工具呢,博主在Unity3D游戏开发之反编译AssetBundle提取游戏资源这篇文章中其实已经提到过了,不过因为有些朋友对如何使用这个工具依然存在问题,所以博主决定特地写一篇文章来讲解如何使用disunity来提取Unity3D游戏中的素材。

    准备工作

    • disunity:负责对Unity3D的数据文件进行解包
    • Unity3D:负责将导出的数据文件显示出来
    • Bleander或者3DsMax:负责Unity3D数据文件的转换处理,二选一即可。个人推荐Blender。
    • Java:负责为disunity提供编译环境

    测试文件

    • 《新仙剑OL》下载
    • 《轩辕剑6外传穹之扉》
    • 《雨血前传:蜃楼》下载

    提取流程

    好了,在确定做好所有的准备工作后,我们就可以正式开始今天的内容了!

    编译disunity

    虽然我们可以从disunity的项目主页中下载release版本,不过为了保险起见,博主依然建议大家自行编译disunity。编译的方法是在命令行中切换到disunity的目录,然后执行命令:

    java -jar disunity.jar

    如果大家的Java环境没有任何问题的话,那么接下来我们就应该可以看到:

    [Info] DisUnity v0.3.4

    以及各种关于这个工具的使用方法和参数选项。那么好了,现在我们就来熟悉下disunity这个工具的常用命令。disunity命令的基本形式是:

    disunity [CommandName] [CommandOptions]

    disunity命令

    • dump:将一个二进制的对象转化成人类可以阅读的文本信息。
    • dump-struct:将一个二进制的对象转化为结构化的信息。
    • extract:将Unity3D的数据文件转化为常见的文本、声音、图片等信息。
    • extract-raw:将Unity3D的数据文件转化为可序列化的对象,在extract命令不被支持的情况下使用。
    • extract-txt:和dump命令类似输出转换结果到命令行。
    • extract-struct:和dump-struct命令类似输出转换结果到命令行。
    • info:输出Unity3D的数据文件和AssetBundle文件的变量信息。
    • bundle-extract:释放所有的被打包到AssetBundle中的文件。
    • bundle-inject:将从AssetBundle中打包的文件重新打包

    暂时先介绍这些,因为其它的命令我们基本用不到,如果需要深入研究这些命令,可以参考disunity项目中的README.md文件。

    解析《新仙剑OL 》的AssetBundle文件

    这里我们以游戏目录/assetbundles/NPC/Models/下的s049.unity3d_CC9026FB为例来讲解游戏模型的提取。

    模型文件提取

    首先我们将这个文件的扩展名改为s049.unity3d,因为这是它原始的扩展名,是Unity3D中导出AssetBundle的一种文件格式。好了,我们将这个文件放在一个无中文路径的目录下,这里以C:\Users\Robin\Desktop即桌面为例。注意首先进入disunity的目录,然后执行命令:

    disunity extract C:\Users\Robin\Desktop\s049.unity3d

    接下来会在桌面生成一个名为s049的文件夹,在这个文件夹中找到Mesh的子文件夹,会得到一个s049.obj的文件,这个文件就是我们提取到的模型文件。

    模型贴图提取

    好了,下面我们再来看看怎么提取这个模型文件对应的贴图,在游戏目录/assetbundles/NPC/Texture/下有一个名为s049_1.unity3d_1D2446B9的文件,这就是s049这个模型对应的贴图了。同样地,我们将其重命名为s049_1.unity3d然后执行命令:

    disunity extract C:\Users\Robin\Desktop\s049_1.unity3d

    接下来在桌面上生成一个名为s049_1的文件夹,在这个文件夹中找到Texture2D的子文件夹,会得到一个名为s049_1.dds的贴图文件,这就是我们要提取的模型s049的贴图文件。

    将模型和贴图合并

    我们打开Blender并将s049.obj文件导入,然后将场景中默认的灯光和摄像机都删除,因为我们只需要一个模型文件,我们发现在Blender中已经可以看到模型了,因为Unity3D中使用的是FBX模型,所以我们这里将模型文件导出为FBX备用。因为Unity3D可以识别dds类型的贴图,所以对贴图我们不用做任何处理。

    童年林月如的模型

    打开Unity3D将童年林月如的模型和贴图一起导入,将童年林月如的模型拖入到游戏场景中,因为模型的尺寸没有经过调整,所以模型刚开始可能会比较小,我们可以在Unity3D进行局部的调整。接下来我们会发现模型没有贴图,只要选择这个模型然后在属性窗口为它附上s049_1.dds的贴图文件即可。下面是童年林月如的模型导入Unity3D以后的效果:

    童年林月如导入Unity3D后的效果

    解析《新仙剑OL》的assets文件

    和AssetBundle不同,assets文件是整个Unity3D项目中项目资源的打包集合,比如说Asset文件下的资源都会被打包到这里,所以说解析assets文件可能会有更大的收获吧!因为所有的Unity3D游戏都会有这样的文件,而AssetBundle文件只有在使用了这项技术的游戏项目中才有。比如说在Unity3D中有一个重要的Resource文件夹,这个文件夹打包后被被打包成resources.assets文件。这里我们以xianjian_Data/resources.assets文件为例。首先执行命名:

    disunity extract C:\Users\Robin\Desktop\resources.assets

    接下来会在桌面生成一个resources的文件夹,打开这个文件夹我们会发现三个子文件夹,分别是Shader、TextAsset和Texture2D。解析的结果似乎有点失望,不过在TextAsset文件夹下我们会找到一个叫做ResourceFiles.txt的文件,这是一个纯文本文件,我们可以直接打开,打开后我们发现它的内容是一个Xml文件,并且在这个Xml文件中定义了游戏中使用的各种资源的路径,不过这些资源都是以AssetBundle的形式来定义的。这说明什么呢?这说明《新仙剑OL》的场景和界面资源是通过动态加载的方式加载到游戏当中的,而这些资源则是通过这个Xml文件来配置和管理的,这符合我们平时在Unity3D游戏开发中的观点和方法。通过这个文件,我们找到了assetbundles/config/movieconfig.unity3d这个文件,这是一个负责维护游戏中场景过场动画的文件。下面我们就来尝试解析这个文件,不过游戏制作方对config文件夹下的内容进行了加密,因为在这个文件夹下面是两个AssetBundle文件,博主尝试用extract和bundle-extract两个命令进行解析,可是得到的只是些文本文件,对我们继续研究没有什么帮助。那么好了,现在我们能够进行解析的只有xinjian_Data/sharedassets0.assets文件了:

    disunity extract C:\Users\Robin\Desktop\sharedassets0.assets

    这个解出来的话是些没有什么用的贴图文件,看来如果要提取音乐或者图片的话,还需要进行更加深入的研究才行啊。

    解析《雨血前传.蜃楼》的assets文件

    因为解析《新仙剑OL》的assets文件没有得到什么有用的东西,所以我们接下来来尝试解析《雨血前传.蜃楼》的assets文件。这款游戏是博主比较喜欢的一款游戏,基于Unity3DY引擎,而且这款游戏是作为Unity3D官方范例来推广的,因此研究这款游戏对我们提高Unity3D的资源打包机制会比较有帮助。好了,我们直接上手:

    disunity extract C:\Users\Robin\Desktop\resources.assets

    哈哈,这款游戏果然没有让我们失望,我们得到了什么呢?

    蜃楼中各种Boss的头像

    蜃楼中游戏连招视频1

    蜃楼中游戏连招视频2

    总结

    • 不同的游戏采用的资源配置方案都不同,不过一般可以从resources.assets这个文件入手作为突破点。
    • 如果能拿到游戏中数据配置方案,对于我们提取游戏中的素材会有较大的帮助,因为这样方向性会更强些。
    • 通过AssetBundle动态加载到场景中最好还是采用一个配置表来进行配置,这样便于我们管理和维护整个游戏项目。
    • 如果没有服务器段的干预,理论上只要修改了本地的AssetBundle文件就可以实现对游戏内容和数据的更改,换句话说,可以做外挂和修改器。

    声明:我不是在教你破解游戏,我只是在研究AssetBundle打包 !

    展开全文
  • 《Android 3D游戏开发技术详解与典型案例》附源码

    千次下载 热门讨论 2012-04-13 14:34:16
    《Android 3D游戏开发技术详解与典型案例》本书主要以Android平台下3D游戏的开发为主题,并结合真实的案例向读者详细介绍了OpenGL ES的基础知识及3D游戏程序开发的整个流程。 全书分为两篇共22章,第一篇以简单易懂...
  • Unity3D游戏开发

    千人学习 2020-12-22 23:57:48
    本课程从初学者角度出发,以低起点,高终点为原则,内容覆盖了Unity游戏开发的所有基础知识。为了让学员不仅能掌握好基础知识,还能够学到实际项目的开发,本课程还配套了两个游戏项目。【课程特点】1  ...
  • Unity3D游戏开发之SQLite让数据库开发更简单

    万次阅读 多人点赞 2015-07-09 09:54:49
    在经历了一段时间的忙碌后,博主终于有时间来研究新的东西啦,今天博客向和大家一起交流的内容是在Unity3D游戏开发中使用SQLite进行数据库开发,坦白来讲,在我的技术体系中Web和数据库是相对薄弱的两个部分,因此...

    各位朋友大家好,欢迎大家关注我的博客,我是秦元培,我是博客地址是http://blog.csdn.net/qinyuanpei。在经历了一段时间的忙碌后,博主终于有时间来研究新的东西啦,今天博客向和大家一起交流的内容是在Unity3D游戏开发中使用SQLite进行数据库开发,坦白来讲,在我的技术体系中Web和数据库是相对薄弱的两个部分,因此正好这段时间项目需要和服务器、数据库进行交互,因此在接下来的文章中博主可能会更加倾向于讲解这方面的内容,希望大家能够喜欢啊!

    一、什么是SQLite?

    SQLite是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中,以嵌入式作为它的设计目标,它占用资源非常的低,因此适合在嵌入式设备如Android、Ruby on Rails等中使用。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟和C、C++、Ruby、Python、C#、PHP、Java等编程语言相结合。SQLite是一个以文件形式存在的关系型数据库,尽管无法实现分布式和横向扩展,可是作为一个轻量级的嵌入式数据库,它不需要系统提供服务支持,通过SDK直接操作文件避免了对数据库维护的相关事务,从这个角度来讲它是一个出色的数据库。

    二、为什么要选择SQLite

    好了,在了解了SQLite后,我们来了解下SQLite有哪些让我们心动的特性,或者说我们为什么要选择SQLite,因为在这个世界上我们有太多的数据库可以选择,诸如Oracle、MySQL、SQLServer、DB2、NoSQL、MongoDB等等:

    • ACID事务
    • 零配置 – 无需安装和管理配置
    • 储存在单一磁盘文件中的一个完整的数据库
    • 数据库文件可以在不同字节顺序的机器间自由的共享
    • 支持数据库大小至2TB
    • 足够小, 大致13万行C代码, 4.43M
    • 比一些流行的数据库在大部分普通数据库操作要快—SQLite读写效率如此之高,会使用其他数据库的理由是?
    • 简单, 轻松的API
    • 包含TCL绑定, 同时通过Wrapper支持其他语言的绑定
    • 良好注释的源代码, 并且有着90%以上的测试覆盖率
    • 独立: 没有额外依赖
    • 源码完全的开源, 你可以用于任何用途, 包括出售它
    • 支持多种开发语言,C, C++, PHP, Perl, Java, C#,Python, Ruby等

    三、Unity3D中的SQLite

    在Unity3D中使用SQLite,我们首先要明白这样一件事情,即我们这里的使用的SQLite并非是通常意义上的SQLite.NET,而是经过移植后的Mono.Data.Sqlite。因为Unity3D基于Mono,因此使用移植后的Mono.Data.Sqlite能够减少我们的项目在不同平台上出现各种各样的问题。在Unity3D中使用的SQLite以Mono.Data.Sqlite.dll即动态链接库的形式给出,因此我们需要将这个文件放置在项目目录下的Plugins文件夹中,此外我们需要System.Data.dll或者Mono.Data.dll这两个文件添加到Plugins目录中,因为我们需要的部分数据相关的API或者类都定义在这两个文件当中,这些文件可以从这里直接下载。

    PS:博主注意到在网上有使用Mono.Data.SQLiteClient.dll这个库实现在Unity3D操作SQLite数据库的相关文章,博主大概看了下,感觉和使用Mono.Data.Sqlite.dll这个库大同小异,大家喜欢哪个就用哪个吧!哈哈!博主在开源社区找到一个版本库,据说可以同时支持.NET和Mono,如果大家感兴趣欢迎大家去测试啊,哈哈!

    在正式开始写代码前,我们首先来回顾下通常情况下数据库读写的基本流程吧!

    • 定义数据库连接字符串(ConnectionString)完成数据库连接的构造,建立或者打开一个数据库。
    • 定义相关的SQL命令(Command)通过这些命令实现对数据库的增加、删除、更新、读取四种基本功能。
    • 在完成各种数据库操作后及时关闭数据库连接,解除对数据库的连接和引用。

    SQLite作为一款优秀的数据库,在为其编写数据库相关代码时同样遵循这样的流程,考虑到对数据库的增加、删除、更新、读取四种操作具有类似性和统一性,因此在动手写Unity3D脚本前,首先让我们来编写一个SQLite的辅助类SQLiteHelper.cs。该类代码定义如下:

    using UnityEngine;
    using System.Collections;
    using Mono.Data.Sqlite;
    using System;
    
    public class SQLiteHelper
    {
        /// <summary>
        /// 数据库连接定义
        /// </summary>
        private SqliteConnection dbConnection;
    
        /// <summary>
        /// SQL命令定义
        /// </summary>
        private SqliteCommand dbCommand;
    
        /// <summary>
        /// 数据读取定义
        /// </summary>
        private SqliteDataReader dataReader;
    
        /// <summary>
        /// 构造函数    
        /// </summary>
        /// <param name="connectionString">数据库连接字符串</param>
        public SQLiteHelper(string connectionString)
        {
            try{
                //构造数据库连接
                dbConnection=new SqliteConnection(connectionString);
                //打开数据库
                dbConnection.Open();
            }catch(Exception e)
            {
                Debug.Log(e.Message);
            }
        }
    
        /// <summary>
        /// 执行SQL命令
        /// </summary>
        /// <returns>The query.</returns>
        /// <param name="queryString">SQL命令字符串</param>
        public SqliteDataReader ExecuteQuery(string queryString)
        {
            dbCommand = dbConnection.CreateCommand();
            dbCommand.CommandText = queryString;
            dataReader = dbCommand.ExecuteReader();
            return dataReader;
        }
    
        /// <summary>
        /// 关闭数据库连接
        /// </summary>
        public void CloseConnection()
        {
            //销毁Command
            if(dbCommand != null){
                dbCommand.Cancel();
            }
            dbCommand = null;
    
            //销毁Reader
            if(dataReader != null){
                dataReader.Close();
            }
            dataReader = null;
    
            //销毁Connection
            if(dbConnection != null){
                dbConnection.Close();
            }
            dbConnection = null;
        }
    
        /// <summary>
        /// 读取整张数据表
        /// </summary>
        /// <returns>The full table.</returns>
        /// <param name="tableName">数据表名称</param>
        public SqliteDataReader ReadFullTable(string tableName)
        {
            string queryString = "SELECT * FROM " + tableName;
            return ExecuteQuery (queryString);
        }
    
        /// <summary>
        /// 向指定数据表中插入数据
        /// </summary>
        /// <returns>The values.</returns>
        /// <param name="tableName">数据表名称</param>
        /// <param name="values">插入的数值</param>
        public SqliteDataReader InsertValues(string tableName,string[] values)
        {
            //获取数据表中字段数目
            int fieldCount=ReadFullTable(tableName).FieldCount;
            //当插入的数据长度不等于字段数目时引发异常
            if(values.Length!=fieldCount){
                throw new SqliteException("values.Length!=fieldCount");
            }
    
            string queryString = "INSERT INTO " + tableName + " VALUES (" + values[0];
            for(int i=1; i<values.Length; i++)
            {
                queryString+=", " + values[i];
            }
            queryString += " )";
            return ExecuteQuery(queryString);
        }
    
        /// <summary>
        /// 更新指定数据表内的数据
        /// </summary>
        /// <returns>The values.</returns>
        /// <param name="tableName">数据表名称</param>
        /// <param name="colNames">字段名</param>
        /// <param name="colValues">字段名对应的数据</param>
        /// <param name="key">关键字</param>
        /// <param name="value">关键字对应的值</param>
        public SqliteDataReader UpdateValues(string tableName,string[] colNames,string[] colValues,string key,string operation,string value)
        {
            //当字段名称和字段数值不对应时引发异常
            if(colNames.Length!=colValues.Length) {
                throw new SqliteException("colNames.Length!=colValues.Length");
            }
    
            string queryString = "UPDATE " + tableName + " SET " + colNames[0] + "=" + colValues[0];
            for(int i=1; i<colValues.Length; i++) 
            {
                queryString+=", " + colNames[i] + "=" + colValues[i];
            }
            queryString += " WHERE " + key + operation + value;
            return ExecuteQuery(queryString);
        }
    
        /// <summary>
        /// 删除指定数据表内的数据
        /// </summary>
        /// <returns>The values.</returns>
        /// <param name="tableName">数据表名称</param>
        /// <param name="colNames">字段名</param>
        /// <param name="colValues">字段名对应的数据</param>
        public SqliteDataReader DeleteValuesOR(string tableName,string[] colNames,string[] operations,string[] colValues)
        {
            //当字段名称和字段数值不对应时引发异常
            if(colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length) {
                throw new SqliteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length");
            }
    
            string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + colValues[0];
            for(int i=1; i<colValues.Length; i++) 
            {
                queryString+="OR " + colNames[i] + operations[0] + colValues[i];
            }
            return ExecuteQuery(queryString);
        }
    
        /// <summary>
        /// 删除指定数据表内的数据
        /// </summary>
        /// <returns>The values.</returns>
        /// <param name="tableName">数据表名称</param>
        /// <param name="colNames">字段名</param>
        /// <param name="colValues">字段名对应的数据</param>
        public SqliteDataReader DeleteValuesAND(string tableName,string[] colNames,string[] operations,string[] colValues)
        {
            //当字段名称和字段数值不对应时引发异常
            if(colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length) {
                throw new SqliteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length");
            }
    
            string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + colValues[0];
            for(int i=1; i<colValues.Length; i++) 
            {
                queryString+=" AND " + colNames[i] + operations[i] + colValues[i];
            }
            return ExecuteQuery(queryString);
        }
    
        /// <summary>
        /// 创建数据表
        /// </summary> +
        /// <returns>The table.</returns>
        /// <param name="tableName">数据表名</param>
        /// <param name="colNames">字段名</param>
        /// <param name="colTypes">字段名类型</param>
        public SqliteDataReader CreateTable(string tableName,string[] colNames,string[] colTypes)
        {
            string queryString = "CREATE TABLE " + tableName + "( " + colNames [0] + " " + colTypes [0];
            for (int i=1; i<colNames.Length; i++) 
            {
                queryString+=", " + colNames[i] + " " + colTypes[i];
            }
            queryString+= "  ) ";
            return ExecuteQuery(queryString);
        }
    
        /// <summary>
        /// Reads the table.
        /// </summary>
        /// <returns>The table.</returns>
        /// <param name="tableName">Table name.</param>
        /// <param name="items">Items.</param>
        /// <param name="colNames">Col names.</param>
        /// <param name="operations">Operations.</param>
        /// <param name="colValues">Col values.</param>
        public SqliteDataReader ReadTable(string tableName,string[] items,string[] colNames,string[] operations, string[] colValues)
        {
            string queryString = "SELECT " + items [0];
            for (int i=1; i<items.Length; i++) 
            {
                queryString+=", " + items[i];
            }
            queryString += " FROM " + tableName + " WHERE " + colNames[0] + " " +  operations[0] + " " + colValues[0];
            for (int i=0; i<colNames.Length; i++) 
            {
                queryString+=" AND " + colNames[i] + " " + operations[i] + " " + colValues[0] + " ";
            }
            return ExecuteQuery(queryString);
        }
    }

    SQLiteHelper类主要实现了数据库、数据表的创建以及数据表中记录的增加、删除、更新、读取四种基本功能。该类最初由国外的Unity3D开发者发布在Unity3D官方论坛,后来经宣雨松使用C#进行重写,我在此基础上进行了完善,再此对两位大神的无私付出表示感谢。这里要说明的有三点:

    • 一、在Unity3D编辑器下生成数据库文件(.db)默认位于和Assets目录同级的位置,即项目的工程文件夹中。我们可以通过修改路径在改变数据库文件的存储位置,具体来讲:
      Windows平台:data source=Application.dataPath/数据库名称.db
      IOS平台:data source=Application.persistentDataPath/数据库名称.db
      Android平台:URL=file:Application.persistentDataPath/数据库名称.db(我想说Android平台就是个奇葩,搞什么特殊化嘛)

    • 二、确保Unity3D编辑器中的.NET版本和MonoDevelop中的.NET版本都为2.0版本,在Unity3D中打包导出的程序可能不会保留数据库文件,因此需要手动将数据库文件拷贝到相应的位置,当然更加合理的方案是将数据库文件存放到StreamingAssets文件夹下,然后在第一次加载游戏的时候将数据库文件复制到对应平台上的存放位置。

    • 三、在使用InsertValues方法时请参考SQLite中字段类型与C#中数据类型的对应关系,博主目前测试了int类型和string类型都没有什么问题,更多类型的数据请大家自行测试然后告诉博主测试的结果,如果大家有兴趣扩展这个辅助类的话可以自行去扩展哦,嘿嘿!

    好了,千呼万唤始出来的时候到了,下面我们以一个实例来完成今天的项目讲解,因为我们已经定义好了SQLite的辅助类,因此我们可以快速地编写出下面的脚本代码:

    using UnityEngine;
    using System.Collections;
    using System.IO;
    using Mono.Data.Sqlite;
    
    public class SQLiteDemo : MonoBehaviour 
    {
        /// <summary>
        /// SQLite数据库辅助类
        /// </summary>
        private SQLiteHelper sql;
    
        void Start () 
        {
            //创建名为sqlite4unity的数据库
            sql = new SQLiteHelper("data source=sqlite4unity.db");
    
            //创建名为table1的数据表
            sql.CreateTable("table1",new string[]{"ID","Name","Age","Email"},new string[]{"INTEGER","TEXT","INTEGER","TEXT"});
    
            //插入两条数据
            sql.InsertValues("table1",new string[]{"'1'","'张三'","'22'","'Zhang3@163.com'"});
            sql.InsertValues("table1",new string[]{"'2'","'李四'","'25'","'Li4@163.com'"});
    
            //更新数据,将Name="张三"的记录中的Name改为"Zhang3"
            sql.UpdateValues("table1", new string[]{"Name"}, new string[]{"'Zhang3'"}, "Name", "=", "'张三'");
    
            //插入3条数据
            sql.InsertValues("table1",new string[]{"3","'王五'","25","'Wang5@163.com'"});
            sql.InsertValues("table1",new string[]{"4","'王五'","26","'Wang5@163.com'"});
            sql.InsertValues("table1",new string[]{"5","'王五'","27","'Wang5@163.com'"});
    
            //删除Name="王五"且Age=26的记录,DeleteValuesOR方法类似
            sql.DeleteValuesAND("table1", new string[]{"Name","Age"}, new string[]{"=","="}, new string[]{"'王五'","'26'"});
    
            //读取整张表
            SqliteDataReader reader = sql.ReadFullTable ("table1");
            while(reader.Read()) 
            {
                //读取ID
                Debug.Log(reader.GetInt32(reader.GetOrdinal("ID")));
                //读取Name
                Debug.Log(reader.GetString(reader.GetOrdinal("Name")));
                //读取Age
                Debug.Log(reader.GetInt32(reader.GetOrdinal("Age")));
                //读取Email
                Debug.Log(reader.GetString(reader.GetOrdinal("Email")));
            }
    
            //读取数据表中Age>=25的所有记录的ID和Name
            reader = sql.ReadTable ("table1", new string[]{"ID","Name"}, new string[]{"Age"}, new string[]{">="}, new string[]{"'25'"});
            while(reader.Read()) 
            {
                //读取ID
                Debug.Log(reader.GetInt32(reader.GetOrdinal("ID")));
                //读取Name
                Debug.Log(reader.GetString(reader.GetOrdinal("Name")));
            }
    
            //自定义SQL,删除数据表中所有Name="王五"的记录
            sql.ExecuteQuery("DELETE FROM table1 WHERE NAME='王五'");
    
            //关闭数据库连接
            sql.CloseConnection();
        }
    }
    

    在上面的代码中我们是在Start方法中创建了数据库和数据表,然而在实际使用中我们需要判断数据库和数据表是否存在,因此如果你使用这段脚本提示错误信息,请确保数据库和数据表是否已经存在。好了,下面的截图展示了程序运行的结果:

    数据库效果演示

    Unity3D效果展示

    作为一个强大的数据库怎么能没有图形化的数据库管理工具呢?所以这里博主向大家推荐一个免安装的小工具SqliteStudio,使用这个工具可以帮助我们方便地管理Sqlite数据库里的数据,这样是不是比较方便呢?哈哈!这个工具可以从这里下载哦!

    SQLiteStudio界面演示

    好了,今天的内容就是这样了,为了写这篇文章花了三个晚上准备,希望大家喜欢啊!如果大家觉得这篇文章有用,请继续关注我的博客,我是秦元培,我的博客地址是http://blog.csdn.net/qinyuanpei

    2015年11月3日更新内容:在不同的平台上数据库的存储位置是不同的,在这里给出一个参考的路径,希望大家在处理移动端的时候注意这些问题啊!

            //各平台下数据库存储的绝对路径(通用)
            //PC:sql = new SQLiteHelper("data source=" + Application.dataPath + "/sqlite4unity.db");
            //Mac:sql = new SQLiteHelper("data source=" + Application.dataPath + "/sqlite4unity.db");
            //Android:sql = new SQLiteHelper("URI=file:" + Application.persistentDataPath + "/sqlite4unity.db");
            //iOS:sql = new SQLiteHelper("data source=" + Application.persistentDataPath + "/sqlite4unity.db");
    
            //PC平台下的相对路径
            //sql = new SQLiteHelper("data source="sqlite4unity.db");
            //编辑器:Assets/sqlite4unity.db
            //编译后:和AppName.exe同级的目录下,这里比较奇葩
            //当然可以用更随意的方式sql = new SQLiteHelper("data source="D://SQLite//sqlite4unity.db");
            //确保路径存在即可否则会发生错误
    
            //如果是事先创建了一份数据库
            //可以将这个数据库放置在StreamingAssets目录下然后再拷贝到
            //Application.persistentDataPath + "/sqlite4unity.db"路径即可
    
    展开全文
  • 今天博主想和大家分享的是Unity3D场景编辑器的扩展开发,相关的话题我们在Unity3D游戏开发之编辑器扩展程序开发实例这篇文章中我们已经有所涉及,今天博主想特别针对场景编辑器的扩展开发来进行下深入研究。...

      今天博主想和大家分享的是Unity3D场景编辑器的扩展开发,相关的话题我们在Unity3D游戏开发之编辑器扩展程序开发实例这篇文章中我们已经有所涉及,今天博主想特别针对场景编辑器的扩展开发来进行下深入研究。对于一个场景编辑器来说,它主要的作用是3D场景视图中实时显示、输入反馈和相关信息的更新。在Unity3D中提供了Editor、EditorWindow、GUILayout、EditorGUILayout、GUIUtility、EditorGUIUtility、Handles、Event等来完成这些工作。其中基于EditorWindow的这种扩展方式我们已经研究过了,这种扩展方式拥有自己的独立窗口使用OnGUI方法进行界面的绘制。今天我们想说的是基于Editor的这种扩展方式,这种扩展方式只能针对脚本,从脚本内容在Inspector里的显示布局到变量在Scene视图的可视化编辑,它都可以完全胜任。这里特别想说的是Handles和Event这两个类,这两个类分别提供了3D显示和输入反馈的功能,我们下面就来学习如何使用这些类来扩展Unity3D的场景编辑器。

    创建一个扩展的Transform组件

      Transform是Unity3D中一个基本的组件,下面我们来创建一个扩展的Transform组件,该组件可以对游戏体的坐标、旋转、缩放进行重置。首先,我们创建一个ExtendTransform的类,该类继承自Editor类:

    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    
    [CustomEditor(typeof(Transform),true)]
    public class ExtendTransform : Editor 
    {
        /// <summary>
        /// Position属性
        /// </summary>
        private SerializedProperty mPos;
    
        /// <summary>
        /// Scale属性
        /// </summary>
        private SerializedProperty mScale;
    
        void OnEnable()
        {
            mPos = serializedObject.FindProperty("m_LocalPosition");
            mScale = serializedObject.FindProperty("m_LocalScale") ;
        }
    
        /// <summary>
        /// Inspector相关GUI函数
        /// </summary>
        public override void OnInspectorGUI()
        {
            EditorGUIUtility.labelWidth = 15;
            //获取最新的可序列化对象
            serializedObject.Update();
            //绘制物体的坐标、旋转和缩放
            DrawPosition();
            DrawRotate();
            DrawScale();
            //更新可序列化对象的属性
            serializedObject.ApplyModifiedProperties();
        }
    
        /// <summary>
        /// 绘制位置
        /// </summary>
        private void DrawPosition()
        {
            GUILayout.BeginHorizontal();
            {
                bool Reset = GUILayout.Button("P", GUILayout.Width(20f));
                EditorGUILayout.LabelField("Position");
                EditorGUILayout.PropertyField(mPos.FindPropertyRelative("x"));
                EditorGUILayout.PropertyField(mPos.FindPropertyRelative("y"));
                EditorGUILayout.PropertyField(mPos.FindPropertyRelative("z"));
                if(Reset) mPos.vector3Value = Vector3.zero;
            }
            GUILayout.EndHorizontal();
        }
    
        /// <summary>
        /// 绘制旋转
        /// </summary>
        private void DrawRotate()
        {
            Vector3 eulerAngles = ((Transform)target).eulerAngles;
            GUILayout.BeginHorizontal();
            {
                bool Reset = GUILayout.Button("R", GUILayout.Width(20f));
                EditorGUILayout.LabelField("Rotation", GUILayout.Width(70f));
                EditorGUILayout.LabelField("X", GUILayout.Width(13f));
                float angleX=EditorGUILayout.FloatField(eulerAngles.x, GUILayout.Width(56f));
                EditorGUILayout.LabelField("Y", GUILayout.Width(13f));
                float angleY = EditorGUILayout.FloatField(eulerAngles.y, GUILayout.Width(56f));
                EditorGUILayout.LabelField("Z", GUILayout.Width(13f));
                float angleZ = EditorGUILayout.FloatField(eulerAngles.z, GUILayout.Width(56f));
                ((Transform)target).eulerAngles = new Vector3(angleX, angleY, angleZ);
                if(Reset)
                {
                    eulerAngles = Vector3.zero;
                    ((Transform)target).eulerAngles = Vector3.zero;
                }
            }
            GUILayout.EndHorizontal();
        }
    
        /// <summary>
        /// 绘制缩放
        /// </summary>
        private void DrawScale()
        {
            GUILayout.BeginHorizontal();
            {
                bool Reset = GUILayout.Button("S", GUILayout.Width(20f));
                EditorGUILayout.LabelField("Scale");
                EditorGUILayout.PropertyField(mScale.FindPropertyRelative("x"));
                EditorGUILayout.PropertyField(mScale.FindPropertyRelative("y"));
                EditorGUILayout.PropertyField(mScale.FindPropertyRelative("z"));
                if (Reset) mScale.vector3Value = Vector3.one;
            }
            GUILayout.EndHorizontal();
        }
    }

      首先我们注意到ExtendTransform继承自Editor,这是我们开发这类编辑器扩展的第一个前提。其次我们注意到在该类的声明位置有这样一个标记:

    [CustomEditor(typeof(Transform),true)]

    该标记表明我们这个编辑器扩展是针对Transform组件进行扩展的,即当物体存在Tranform组件时会在编辑器中响应这个编辑器扩展程序。我们在这个编辑器扩展程序中都做了哪些事情呢?第一,我们实现了OnEnable()方法,该方法相当于一个初始化的方法;第二,我们重写了OnOnInspectorGUI()方法,该方法将覆盖默认的Inspector窗口外观。

    扩展后的Transform

    好了,现在我们点击场景中默认的相机MainCamera可以发现默认的Transform会变成具有重置功能的扩展型Transform。下面我们来介绍这段程序中较为重要的核心内容:

    Unity3D中的可序列化对象

      通常我们所说的序列化是指将一个对象的实例转化为字符串的过程,而在Unity3D中可序列化对象更像是一种智能对象,它可以将脚本中的属性显示在Inspector窗口中,当场景发生变化时这些属性值将自动被更新。例如我们可以定义这样一个简单的脚本:

    /// <summary>
    /// 定义一个可序列化类
    /// </summary>
    [System.Serializable]
    public class ExampleClass 
    {
        [SerializeField]
        public int ID;
        [SerializeField]
        public string Name;
        [SerializeField]
        public Vector3[] Points;
    
        private bool editable = false;
    }
    
    /// <summary>
    /// 定义一个简单的脚本
    /// </summary>
    public class ExampleScript : MonoBehaviour 
    {
        public ExampleClass Example;
    }

    此时如果我们给场景中的某个物体附加上该脚本,则我们在Inspector窗口可以看到Example类的实例Example将被序列化到编辑器面板中,同时我们可以注意到私有的editable字段并没有被序列化出来,这是因为在Unity3D中,公有的字段默认支持序列化,私有的字段除非显式的增加[SerializeField]标记,否则都不会被序列化,这一点希望大家注意。好了,那么我们为什么要讲这部分内容呢,这是因为它和我们下面要讲的Editor基类中的属性和方法有着十分密切的关联。

    Unity3D中的可序列化对象

    Editor基类中的属性和方法

      Editor基类中有两个重要的属性,即target和serializedObject。target表示当前受检查的物体我们可以通过它获得当前物体;而serializedObject表示当前物体的全部可序列化信息,我们可以通过它获得指定的序列化字段及其数值。Editor基类中重要的方法有:
    * OnInspectorGUI():该方法可对Inspector窗口面板进行扩展或者重写,比如我们可以通过DrawDefaultInspector()方法来绘制默认Inspector窗口面板然后在此基础上使用GUILayout或者EditorGUILayout等辅助类进行自定义的绘制。在这个示例中我们对整个面板进行了重写,值得注意的是为了让Inspector窗口面板正常工作,如果要重绘该窗口请确保对该方法进行覆盖。
    * OnSceneGUI():该方法可对场景视图进行绘制,在实际的使用中可以配合Handles类和Event类来进行网格编辑、地形绘制或高级Gizmos等方面的工作。在本文的第二个示例中,我们将利用这一特性来编写一个用于NPC寻路的路径节点编辑工具。

    对第一个示例的总结

      在第一个示例中,可以注意到我们使用了FindProperty()方法来获取一个可序列化物体的属性(字段),然后我们在EditorGUILayout.PropertyField()方法来绘制了各种属性框,这种方式可以实现属性的自动更新。注意到DrawRotate()方法与DrawPositin()及DrawScale()方法在实现方式上略有不同,这是因为Transform组件的Rotation属性是一个Quaternion即四元数的结构,四元数是利用x、y、z、w四个数值来表示物体的三维旋转,这不仅和我们平时习惯的欧拉角相违背而且更为关键的是貌似目前我还没有发现可以直接绘制四元数的API接口,如果有的话希望大家可以告诉我,所以这里我们用了变通的一种方法,即通过Transform的eulerAngles来实现,但是这种方式绘制的属性框大小和EditorGUILayout.PropertyField()方法绘制的属性框大小并不一致,同时我们需要自己去完成属性值的更新。好了,暂时先总结到这里更多的细节大家可以通过代码来了解。

    创建一个NPC寻路节点编辑工具

      创建这样一个工具的想法来自我实际的工作体验,当我Unity3D中使用的Tween动画库从iTween变成Dotween后,我在使用Dotween的过程中一直没有找到类似于iTweenPath的路径节点编辑工具。作为一个有节操的程序员,去寻找破解版的Dotween Pro这样的事情我是能不干就不干啦,因为我觉得自己有能力做这样一个类似的小工具,所以在一边准备这篇文章的时候,一边开始设计这样一个路径节点编辑工具。相信经过第一个示例的铺垫和相关知识的储备,大家都了解了这些内容,所以这里直接给出代码啦,因为实在是没有多少内容,嘿嘿:

    using UnityEngine;
    using System.Collections;
    using UnityEditor;
    
    [CustomEditor(typeof(PatrolNPC))]
    public class PatrolPathEditor : Editor 
    {
        /// <summary>
        /// 寻路节点
        /// </summary>
        private Vector3[] paths;
    
        /// <summary>
        /// 显示寻路信息的GUI
        /// </summary>
        private GUIStyle style=new GUIStyle();
    
        /// <summary>
        /// 初始化
        /// </summary>
        void OnEnable()
        {
            //获取当前NPC的寻路路径
            paths = ((PatrolNPC)target).Paths;
            //初始化GUIStyle
            style.fontStyle = FontStyle.Normal;
            style.fontSize = 15;
        }
    
    
        void OnSceneGUI()
        {
            //获取当前NPC的寻路路径
            paths = ((PatrolNPC)serializedObject.targetObject).Paths;
            //设置节点的颜色为红色
            Handles.color = Color.red;
            if(paths.Length <= 0 || paths.Length<2) return;
            //在场景中绘制每一个寻路节点
            //可以在场景中编辑节点并将更新至对应的NPC
            for (int i = 0; i < paths.Length; i++)
            {
                paths[i] = Handles.PositionHandle(paths[i], Quaternion.identity);
                Handles.SphereCap(i, paths[i], Quaternion.identity, 0.25f);
                Handles.Label(paths[i], "PathPoint" + i, style);
                if (i < paths.Length && i + 1 < paths.Length)
                {
                    Handles.DrawLine(paths[i], paths[i + 1]);
                }
            }
        }
    
    }

      这里的PatrolNPC是一个可寻路NPC类,基本和这篇文章的内容无关,大家只要知道那个Paths字段是一个Vector3[]就好啦,这样当我们在场景中编辑这些路径节点的时候,对应NPC的路径节点信息就会同步发生更新,这样我们就可以随心所欲地规划NPC的移动路径啦,哈哈。好了,今天的内容就是这样啦,写完熬到这个点真心不容易啊,大家晚安,这是这个小工具在场景编辑器中的效果,嘻嘻,感觉还是蛮不错的吧,反正我是很喜欢就对啦!

    路径节点编辑工具演示

    展开全文
  • unity3D游戏开发

    2019-08-09 13:43:06
    本课程将带着大家学习unity3d中shader的基础内容!!!
  • Unity3D游戏开发之使用Unity3D开发2D游戏 (一)

    万次阅读 热门讨论 2014-03-17 09:14:17
    今天要和大家分享的是基于Unity3D开发2D游戏,博主一直钟爱于国产武侠RPG,这个我在开始写Unity3D游戏开发系列文章的时候就已经说过了,所以我们今天要做的就是利用Unity3D来实现在2D游戏中人物的走动控制。...

            今天要和大家分享的是基于Unity3D开发2D游戏,博主一直钟爱于国产武侠RPG,这个我在开始写Unity3D游戏开发系列文章的时候就已经说过了,所以我们今天要做的就是利用Unity3D来实现在2D游戏中人物的走动控制。下面就请大家跟随我的一起来见证Unity3D强大的魅力吧。首先我们创建一个项目,创建新场景,我们首先创建一个Plane并将其命名为MainMap,然后我们在项目中添加一个Resources文件夹,将事先准备好的地图文件Map0复制到这个目录下,然后我们将这个图片拖拽到MainMap上,我们将MainMap的旋转参数Rotatiion设置为90,180,0,使其与摄像机垂直,然后将摄像机的Projection投影属性设置为 orthographic正交模式,这时我们就能够在屏幕中看到经典的余杭镇场景了。如果我们需要对场景进行进一步的优化的话,可以通过调整摄像机的投影面积大小和距离来实现。参数设置可以参考这里:



           接下来,我们再次创建一个Plane将其命名为Player,这就是我们今天要出场的角色李逍遥啦,和准备地图类似,我们将李逍遥的贴图直接拖拽到Player上。刚开始角色会比较大,和画面不协调,我们可以通过调整Scale参数来缩放我们的角色,调整到合适的范围。这里我们直接使用了一张完整的地图,在实际的游戏开发中,一个场景地图可能是由很多瓦片构成的,所谓瓦片,就是指被分割成若干个可以拼接起来的图片。我们知道,仙剑奇侠传作为一款经典的单机游戏,除了剧情以外,就是那些经典、动听的音乐啦,在Unity3D中实现音乐播放是很简单的事情,我们选定MainMap,为主场景添加一个AudioSource组件,并将背景音乐拖拽到AudioSource的AudioClip属性框中,选中Loop使其循环,接下来我们点击运行,运行效果如下图:


           接下类,就是今天的重头戏啦,我们今天的主要任务是通过Unity3D的2D帧动画绘制来实现人物的动画和控制,所以接下来我们在项目目录下增加一个Script文件下,新建一个叫做Person的脚本,该脚本封装了我们对人物角色的控制和帧动画的绘制方法。首先我们来准备一组素材:





               从名称的命名上,我们就可以看出这是角色在上下左右四个方向上的连续动画图片。下面,我们在Resources目录下创建下面的目录结构,并将上面的图片分别放在对应的文件夹下,我们在脚本中会引用下面的资源:


             下面开始编写脚本:

     

    using UnityEngine;
    using System.Collections;
    
    public class Person : MonoBehaviour {
    	//定义角色对象
    	private GameObject mHero;
    	//定义人物向上动画数组
    	private Object[] UpAnim;
    	//定义人物向下动画数组
    	private Object[] DownAnim;
    	//定义人物向左动画数组
    	private Object[] LeftAnim;
    	//定义人物向右动画数组
    	private Object[] RightAnim;
    	//当前帧索引
    	private int mFrameIndex;
    	//当前时间
    	private float mTotalTime;
    	//定义每秒帧数为10
    	private float mFPS=10;
    	//定义当前人物动画
    	private Object[] NowAnim;
    	private Object[] LastAnim;
    	//定义角色行动速度
    	private float mSpeed=5;
    	//角色当前状态
    	private PersonState mState=PersonState.Stand;
    	
    	//定义角色状态枚举
    	private enum PersonState
    	{
    	   Stand,
    	   Walk
    	}
    	
    	void OnGUI()
    	{
    	   GUI.Label(new Rect(10,10,180,40),"李逍遥位置:"+mHero.transform.position.ToString());
    	}
    	
    	void Start () 
    	{
    	   //获取人物
    	   mHero=GameObject.Find("Player");
    	   //获取动画数组
    	   UpAnim=Resources.LoadAll("UpAnim");
    	   DownAnim=Resources.LoadAll("DownAnim");
    	   LeftAnim=Resources.LoadAll("LeftAnim");
    	   RightAnim=Resources.LoadAll("RightAnim");
    	   //定义默认动画
    	   NowAnim=UpAnim;
    	   LastAnim=NowAnim;
    	}
    	
    	void FixedUpdate()
    	{
    	   //向左
    	   if(Input.GetKey(KeyCode.A))
    	   {
    	      SetAnimation(LeftAnim);
    	      this.mState=PersonState.Walk;
    	      mHero.transform.Translate(Vector3.right*Time.deltaTime*mSpeed);
    	   }
    	   //向右
    	   if(Input.GetKey(KeyCode.D))
    	   {
    	      SetAnimation(RightAnim);
    	      this.mState=PersonState.Walk;
    	      mHero.transform.Translate(Vector3.right*Time.deltaTime*(-mSpeed));
    	   }
    	   //向上
    	   if(Input.GetKey(KeyCode.W))
    	   {
    	      SetAnimation(UpAnim);
    	      this.mState=PersonState.Walk;
    	      mHero.transform.Translate(Vector3.forward*Time.deltaTime*(-mSpeed));
    	   }
    	   //向下
    	   if(Input.GetKey(KeyCode.S))
    	   {
    	      SetAnimation(DownAnim);
    	      this.mState=PersonState.Walk;
    	      mHero.transform.Translate(Vector3.forward*Time.deltaTime*mSpeed);
    	      Vector3 mHeroPos=mHero.transform.position;
    	   }
    	   DrawTexture(NowAnim);
    	}
    	
    	void Update () 
    	{
    	   //松开键位时
    	   if(Input.GetKeyUp(KeyCode.A) || Input.GetKeyUp(KeyCode.D) 
    	   || Input.GetKeyUp(KeyCode.S) || Input.GetKeyUp(KeyCode.W))
    	   {
    	      this.mState=PersonState.Stand;
    	   }
    	}
    	
    	void DrawTexture(Object[] mAnim)
    	{
    	   //如果角色当前状态为站立,则显示帧动画中的第一帧
    	   if(mState==PersonState.Stand)
    	   {
    	     mHero.renderer.material.mainTexture=(Texture)mAnim[0];
    	   }
    	   //否则绘制帧动画
    	   else
    	   {
    	      mTotalTime+=Time.deltaTime;
    	      if(mTotalTime>=1.0/mFPS)
    	      {
    	         mFrameIndex++;
    	         mTotalTime=0;
    	         if(mFrameIndex>=mAnim.Length)
    	         {
    	           mFrameIndex=0;
    	         }
    	      }
    	        mHero.renderer.material.mainTexture=(Texture)mAnim[mFrameIndex];
    	   }
    	}
    	
    	void SetAnimation(Object[] mAnim)
    	{
    	   NowAnim=mAnim;
    	   if(!LastAnim.Equals(NowAnim))
    	   {
    	      this.mFrameIndex=0;
    	      LastAnim=NowAnim;
    	   }
    	}
    }
    

             在上面的这部分脚本中,我们在Start()方法中加载了我们预先设定好的资源,然后在FixedUpdate()方法判断用户行为,并根据用户按下的键来确定要绘制的帧动画,我们假定每秒的帧数为10,则每帧显示的时间为0.1秒,如果从上一次刷新到现在的时间超过或者等于这个时间,则表示我们需要更新帧画面了。而当帧画面的索引和该帧动画数组内元素个数相等的时候,则意味着开始循环绘制这组帧动画。我们在上面定义了一个人物状态的枚举类型,这里只是简单的定义了两种类型,即Stand和Walk类型,当用户按下控制键时此类型的值为Walk,当用户松开控制键时此类型的值为Stand,和上一篇文章不同,这里调用的是 FixedUpdate()方法,而不是Update()方法,原因是Update()f方法更新的时间我们无法控制,而FixedUpdate()方法的时间则可以通过Edit->Project Setting->time->Fixed Timestep来指定,具体的大家可以自己阅读代码,这里就不多说了,剩下的事情就是把脚本绑定到Player上,我们运行一下:



             就目前这个解决方案而言,存在以下几个问题:

             1、人物范围控制,换句话说就是如何保证人物不会从这些房屋、篱笆上直接穿过去

             2、人物向下移动时,会钻到地底下

             3、摄像机如何跟随人物实现平移

             4、小地图的实现

              5、场景地图范围控制

              针对以上问题,伴随着博主学习Unity3D的深入,我们将在后面的文章中一一为大家解决,请大家关注我的博客,谢谢!


    展开全文
  • Unity3D游戏开发之邂逅Unity3D

    千次阅读 热门讨论 2014-03-16 01:32:43
    所以,从今天起,大家将看到由我为大家带来的Unity3D系列文章,让我们一起来学习Unity3D游戏开发吧!  在正式今天的文章之前,博主想简单介绍下博主自己,博主是一个喜欢仙剑、古剑的武侠类RPG游戏玩家,博主喜欢...
  • 2018年Unity3D游戏开发前景预测

    万次阅读 2018-01-18 17:58:33
    随着移动手机游戏盛行,游戏开发人才供不应求,如何成为一名优秀的开发者?掌握Unity3D开发技术是...学习Unity3D游戏开发,未来职业发展前景非常可观。Unity 3D是什么?Unity 3D到底是什么?如此受欢迎,市场占有率如此
  • [Unity3D]Unity3D游戏开发之跑酷游戏项目讲解

    万次阅读 多人点赞 2014-05-11 00:24:02
    大家晚上好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/...所以,今天的博客的主题就是《Unity3D游戏开发之跑酷游戏项目讲解》。从博主自身来考虑这件事情,当你选择做自己热爱的事情的时
  • DirectX9.0+3D游戏开发编程基础(PDF+源码)

    千次下载 热门讨论 2012-10-04 15:00:23
    DirectX9.0+3D游戏开发编程基础(PDF+源码) 适合想学习DirectX9技术的人看 随书代码 MSVC2007编译通过。
  • 3D游戏开发之基础建模

    千次阅读 2013-12-28 13:28:35
    3d游戏开发来说建模是非常重要的部分,即使是代码开发人员也应该掌握一些建模的常用工具,这样才能在3D开发过程中做到游刃有余。 3D MAX是这样一款非常流行的3d建模软件
  • Unity3D游戏开发之如何发布Android游戏

    千次阅读 2016-01-12 18:25:50
    怎么在Unity3D游戏开发软件里,发布Android游戏?如果你感到有疑问,不如看看下面笔者的介绍。
  • 在学习《Unity 3D 游戏开发》这本书的过程中碰到了一些术语,比如序列化、反序列化、反射等,这些基本都是常见的程序员行话,但是感觉自己对这些术语总是一知半解,为了让自己更清楚地知道这些术语,本文对其进行了...
  • 5个最佳的3D游戏开发工具

    千次阅读 2018-11-08 09:13:14
    5个最佳的3D游戏开发工具
  • 框架清晰,三维效果ok,适用于所有人学习3D游戏开发。 【注】GetDeviceCaps(hDC, LOGPIXELSY) 用于取得每英寸有多少像素 MulDiv函数(被乘数, 分子, 分母) = nPointSize * GetDeviceCaps(hDC, LOGPIXELSY) / 72...
  • Unity3D游戏开发之3DMAX 灯光

    千次阅读 2015-03-10 18:07:42
    Unity3D游戏开发之3DMAX 灯光 欢迎来到unity学习、unity培训、unity企业培训教育专区,这里有很多U3D资源、U3D培训视频、U3D教程、U3D常见问题、U3D项目源码,【狗刨学习网】unity极致学院,致力于打造业内unity3d...
  • [Unity3D]Unity3D游戏开发之从Unity3D到Eclipse

    万次阅读 热门讨论 2014-10-01 18:17:38
    今天呢,博主将为大家送上Unity-Android系列的最后一篇文章《Unity3D游戏开发之从Unity3D到Eclipse》。通过前面的学习,大家已经知道通过在Eclipse中为Unity编写插件的方法,我们可以实现在Unity与Android API的通信...
  • [Unity3D]Unity3D游戏开发之截屏保存精彩瞬间

    万次阅读 多人点赞 2014-09-10 20:28:00
    各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/...比如博主在写《[Unity3D]Unity3D游戏开发之自由视角下的角色控制》和《[Unity3D]Unity3D游戏开发之角色控制漫谈》这两篇
  • 零基础unity3d游戏开发系列目录

    千次阅读 多人点赞 2014-07-10 16:55:51
    零基础Unity3D游戏开发系列 第一章:游戏开发与游戏引擎(一) 零基础Unity3D游戏开发系列 第一章:游戏开发与游戏引擎(二) 零基础Unity3D游戏开发系列 第二章:Unity3D概览(一)界面... ...
  • 2019该怎么学unity3D游戏开发

    千次阅读 2019-01-22 10:18:24
    Unity 3D本来是由德国的一些...怎么学unity3d游戏开发? 首先要了解U3D最重要的五大界面: 第一:场景(Sence),构建游戏的地方; 第二:层级(Hierarchy),场景中的游戏对象都列在这里。 第三:检测面板(Inspect...
  • Unity3D游戏开发之虚拟现实项目开发流程  欢迎来到unity学习、unity培训、unity企业培训教育专区,这里有很多U3D资源、U3D培训视频、U3D教程、U3D常见问题、U3D项目源码,【狗刨学习网】unity极致学院,致力于打造...
  • [Unity3D]Unity3D 游戏开发之碰撞检测

    万次阅读 2014-04-07 12:43:03
    大家好,欢迎大家关注由我为大家带来的Unity3D游戏开发系列文章,我的博客地址为:http://blog.csdn.net/qinyuanpei。 今天我们来一起来学习Unity3D中一个很重要的概念:碰撞。为什么说碰撞很重要呢?因为在游戏中...
  • [Unity3D]Unity3D游戏开发之粒子系统

    千次阅读 2014-04-07 18:23:05
    大家好,欢迎大家关注由我为大家带来的Unity3D游戏开发系列文章,我的博客地址为:http://blog.csdn.net/qinyuanpei。  今天为大家分享的是Unity3D中的粒子系统,粒子系统通常用来表现烟雾、云等高级效果,是一个...
  • Unity3D游戏开发创建桌面快捷方式

    千次阅读 2016-01-13 17:13:59
    Unity3D游戏开发的软件功能可是越来越好了,功能越来越完善了,不仅是发布Android平台,还能发布IOS平台、PC平台,而今天我主要是来讲讲在Unity3D中创建快捷方式并添加到Launch Panel。
  • 对于编程零基础小白来说,Unity3D游戏开发真的是高深到无法踏足的领域吗?我认为并不是这样的。 按照我个人的理解:其实Unity本身并不是一种体系,而是计算机图形学+游戏开发+各种其他系统的一种表现。学习进阶游戏...
  • 学习3D游戏开发进阶之路(完整版)

    千次阅读 2016-12-27 08:58:59
    **由于头条号链接出现问题,现将内容迁移过来。** 笔者从事IT行业15年了,一直奋斗在一线编程,从普通程序员逐步成长到上市公司技术总监,目前在创业公司担任技术合伙人,主要负责公司整个项目...学习3D游戏开发我...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 96,245
精华内容 38,498
关键字:

3d游戏开发