2016-10-14 01:50:18 yuxikuo_1 阅读数 2269
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

工具类

using UnityEngine;
using UnityEditor;
using System.IO;

public static class ScriptableObjectUtility
{
    public static void CreateAsset<T>() where T : ScriptableObject
    {
        T asset = ScriptableObject.CreateInstance<T>();
        string path = AssetDatabase.GetAssetPath(Selection.activeObject);
        if( string.IsNullOrEmpty( path ) )
        {
            Debug.LogError("Not select files, select files first! ");
            return;
        }
        else if (!string.IsNullOrEmpty(Path.GetExtension(path)))
        {
            path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
        }
        string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/New" + typeof(T).ToString() + ".asset");
        AssetDatabase.CreateAsset(asset, assetPathAndName);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        EditorUtility.FocusProjectWindow();
        Selection.activeObject = asset;
    }
}
菜单按钮
using UnityEngine;
using UnityEditor;
using System;
public class CreateAssets
{
    [MenuItem("Assets/Create assets")]
    public static void CreateAsset()
    {
        ScriptableObjectUtility.CreateAsset<AssetsScripts>();
    }
}
被生成对象,ScriptableObject类型

using UnityEngine;
using UnityEditor;
using System;
public class AssetsScripts : ScriptableObject
{
    public string playerName = "Yu Xikuo";
}


2019-03-04 09:19:39 qq_32141141 阅读数 43
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

Unity3d 调用IOS相册

最近在做项目的时候需要调用IOS相册,作为IOS小白的我也试了下,中间也遇到一些坑,最后还是弄好打包真机测试成功,虽然做出来很快,但是要真的弄清Ios和Unity3d的互通消息机制还是需要慢慢摸索的。

软件环境

 1.Unity3d 2018.3.0f2
 2.Xcode10.1

.h/ .m文件编写

在U3d Assets下面的Plugins\IOS下面创建IOSCameraController.h 和IOSCameraController.m文件。创建可以先创建文本然后更改后缀名。
IOSCameraController.h文件

 //import 引用头文件 相当远Using 
#import<QuartzCore/CADisplayLink.h>
//声明一个IOSCameraController类  继承自UIViewController <>里面是是协议/代理的调用声明 可以理解为c#的接口
@interface IOSCameraController : UIViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@end

IOSCameraController.m文件

#import "IOSCameraController.h"
@implementation IOSCameraController
-(void)OpenTarget:(UIImagePickerControllerSourceType)type{
   //创建UIImagePickerController实例
   UIImagePickerController *picker;
   picker= [[UIImagePickerController alloc]init];
   //设置代理
   picker.delegate = self;
   //是否允许编辑 (默认为NO)
   picker.allowsEditing = YES;
   //设置照片的来源
   // UIImagePickerControllerSourceTypePhotoLibrary,      // 来自图库
   // UIImagePickerControllerSourceTypeCamera,            // 来自相机
   // UIImagePickerControllerSourceTypeSavedPhotosAlbum   // 来自相册
   picker.sourceType = type;
   
   //这里需要判断设备是iphone还是ipad  如果使用的是iphone并没有问题 但是如果 是在ipad上调用相册获取图片 会出现没有确定(选择)的按钮 所以这里判断
   //了一下设备,针对ipad 使用另一种方法 但是这种方法是弹出一个界面 并不是覆盖整个界面 需要改进 试过另一种方式 重写一个相册界面 
   //(QQ的ipad选择头像的界面 就使用了这种方式 但是这里我们先不讲 (因为我也不太懂 但是我按照简书的一位老哥的文章写出来了 这里放一下这个简书的链接
   //https://www.jianshu.com/p/0ddf4f7476aa)
   if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary &&[[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
       // 设置弹出的控制器的显示样式
       picker.modalPresentationStyle = UIModalPresentationPopover;
       //获取这个弹出控制器
       UIPopoverPresentationController *popover = picker.popoverPresentationController;
       //设置代理
       popover.delegate = self;
       //下面两个属性设置弹出位置
       popover.sourceRect = CGRectMake(0, 0, 0, 0);
       popover.sourceView = self.view;
       //设置箭头的位置
       popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
       //展示选取照片控制器
       [self presentViewController:picker animated:YES completion:nil];
   } else {
       //展示选取照片控制器
       [self presentViewController:picker animated:YES completion:^{}];
   }
  
}
//选择完成,点击界面中的某个图片或者选择(Choose)按钮时触发
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
   //关闭界面
   [picker dismissViewControllerAnimated:YES completion:^{}];
   //得到照片
   UIImage *image = [info objectForKey:@"UIImagePickerControllerEditedImage"];
   if (image == nil) {
       image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
   }
   //有些时候我们拍照后经常发现导出的照片方向会有问题,要么横着,要么颠倒着,需要旋转才适合观看。但是在ios设备上是正常的
   //所以在这里处理了图片  让他旋转成我们需要的
   if (image.imageOrientation != UIImageOrientationUp) {
   //图片旋转
       image = [self fixOrientation:image];
   }
   //获取保存图片的地址
   NSString *imagePath = [self GetSavePath:@"Temp.jpg"];
   //保存图片到沙盒路径 对应unity中的Application.persistentDataPath 之后我们取图片就需要在这个路径下取  这是一个可读可写的路径
   [self SaveFileToDoc:image path:imagePath];
}
//获取保存文件的路径 如果有返回路径 没有创建一个返回
-(NSString*)GetSavePath:(NSString *)filename{
   NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
   NSString *docPath = [pathArray objectAtIndex:0];
   return [docPath stringByAppendingPathComponent:filename];
}
//将图片保存到沙盒路径
-(void)SaveFileToDoc:(UIImage *)image path:(NSString *)path{
   NSData *data;
   if (UIImagePNGRepresentation(image)==nil) {
       data = UIImageJPEGRepresentation(image, 1);
   }else{
       data = UIImagePNGRepresentation(image);
   }
   [data writeToFile:path atomically:YES];
   //保存之后通知unity 执行对应的回调 
   //UnitySendMessage 是用来给unity发消息的  有三个参数 1.挂载对应回调脚本的物体名 2.回调函数的名称 3.对应回调上的参数
   UnitySendMessage("Canvas", "Message", "Temp.jpg");
}
#pragma mark 图片处理方法
//图片旋转处理
- (UIImage *)fixOrientation:(UIImage *)aImage {
   CGAffineTransform transform = CGAffineTransformIdentity;
   
   switch (aImage.imageOrientation) {
       case UIImageOrientationDown:
       case UIImageOrientationDownMirrored:
           transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
           transform = CGAffineTransformRotate(transform, M_PI);
           break;
           
       case UIImageOrientationLeft:
       case UIImageOrientationLeftMirrored:
           transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
           transform = CGAffineTransformRotate(transform, M_PI_2);
           break;
           
       case UIImageOrientationRight:
       case UIImageOrientationRightMirrored:
           transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
           transform = CGAffineTransformRotate(transform, -M_PI_2);
           break;
       default:
           break;
   }
   
   switch (aImage.imageOrientation) {
       case UIImageOrientationUpMirrored:
       case UIImageOrientationDownMirrored:
           transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
           transform = CGAffineTransformScale(transform, -1, 1);
           break;
           
       case UIImageOrientationLeftMirrored:
       case UIImageOrientationRightMirrored:
           transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
           transform = CGAffineTransformScale(transform, -1, 1);
           break;
       default:
           break;
   }
   
   // Now we draw the underlying CGImage into a new context, applying the transform
   // calculated above.
   CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
                                            CGImageGetBitsPerComponent(aImage.CGImage), 0,
                                            CGImageGetColorSpace(aImage.CGImage),
                                            CGImageGetBitmapInfo(aImage.CGImage));
   CGContextConcatCTM(ctx, transform);
   switch (aImage.imageOrientation) {
       case UIImageOrientationLeft:
       case UIImageOrientationLeftMirrored:
       case UIImageOrientationRight:
       case UIImageOrientationRightMirrored:
           // Grr...
           CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
           break;
           
       default:
           CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
           break;
   }
   // And now we just create a new UIImage from the drawing context
   CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
   UIImage *img = [UIImage imageWithCGImage:cgimg];
   CGContextRelease(ctx);
   CGImageRelease(cgimg);
   return img;
}
@end
//由于C++编译器需要支持函数的重载,会改变函数的名称,因此dll的导出函数通常是标准C定义的。
//这就使得C和C++的互相调用变得很常见。但是有时可能又会直接用C来调用,不想重新写代码,
//让标准C编写的dll函数定义在C和C++编译器下都能编译通过,通常会使用以下的格式:(这个格式在很多成熟的代码中很常见)
#if defined(__cplusplus)
extern "C" {
#endif
   //导出接口供unity使用
   void IOS_OpenCamera(){
       IOSCameraController *app = [[IOSCameraController alloc]init];
       UIViewController *vc = UnityGetGLViewController();
       [vc.view addSubview:app.view];
       [app OpenTarget:UIImagePickerControllerSourceTypeCamera];
   }
   void IOS_OpenAlbum(){
       IOSCameraController *app = [[IOSCameraController alloc]init];
       UIViewController *vc = UnityGetGLViewController();
       [vc.view addSubview:app.view];
       [app OpenTarget:UIImagePickerControllerSourceTypePhotoLibrary];
   }
#if defined(__cplusplus)
}
#endif

unity3d 调用脚本IOSAlbumCamera.cs ,开始准备用www加载本地图片,但是好像www和UnityWebRequest,但是这个在IOS上面会报错,具体错误下方贴出,所以用了万能的IO加载

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using UnityEngine.Networking;
using System.IO;

public class IOSAlbumCamera : MonoBehaviour
{
   [SerializeField] private Button _openCamera; //打开相机按钮
   [SerializeField] private Button _openAlbum; //打开相册按钮
   [SerializeField] private RawImage _image; //用于显示的图片
   //引入在oc中定义的那两个方法
   [DllImport("__Internal")]
   private static extern void IOS_OpenCamera();
   [DllImport("__Internal")]
   private static extern void IOS_OpenAlbum();

   void Awake()
   {
       //为两个button添加点击事件
       _openCamera.onClick.AddListener(IOS_OpenCamera);
       _openAlbum.onClick.AddListener(IOS_OpenAlbum);
   }
   //ios回调unity的函数
   void Message(string filenName)
   {
       Debug.Log("<<<<<<<<<<<<<<<<<<回调>>>>>>>>>>>>>>>>>>>>>>>");
       Debug.Log("fileName:     " + filenName);
       //我们传进来的只是文件名字 这里合成路径
       string filePath = Application.persistentDataPath + "/" + filenName;
       Debug.Log("filePath:         " + filePath);
       //开启一个协程加载图片

       double startTime = (double)Time.time;
       //创建文件读取流
       FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
       fileStream.Seek(0, SeekOrigin.Begin);
       //创建文件长度缓冲区
       byte[] bytes = new byte[fileStream.Length];
       //读取文件
       fileStream.Read(bytes, 0, (int)fileStream.Length);
       //释放文件读取流
       fileStream.Close();
       fileStream.Dispose();
       fileStream = null;

       //创建Texture
       int width = 300;
       int height = 372;
       Texture2D texture = new Texture2D(width, height);
       texture.LoadImage(bytes);

       _image.texture = texture;




       //StartCoroutine(HttpGetTexture(filePath));
   }


   IEnumerator HttpGetTexture(string url)
   {
       Debug.Log("HttpGetTexture1111111111111111111111");
       WWW www = new WWW(url);

       if (!www.isDone)
           yield return www;
       else
       {
           Debug.Log("2222222222222222222222222" );
           if (www.error == null)
           {
               Debug.Log("3333333333333333333333333333333333333");
               Texture2D texture2D = new Texture2D(128, 128);
               texture2D.LoadImage(www.bytes);
               _image.texture = texture2D;
           }
           else
               Debug.LogError("Error:    " + www.error);
       }
   }
}

导出Xcode,打包真机调试。

1.在Build的时候[图片] MapFileParser.sh: Permission denied

[图片]错误信息

解决方法,打开ios的终端,运行chmod +x /Users/ccc/Desktop/Album 下午10.47.06/MapFileParser.sh

/User后面为MapFileParser.sh 所在的地方,根据自己的地方填写正确。

2.在真机运行的时候,用www和UnityWebRequest报错,改用IO加载图片。报错的原因是iOS9对应用通讯安全策略进行了升级, 已不再支持http这种不安全的协议,新特性要求App内访问的网络必须使用HTTPS协议。当然也可以使用Http请求,但是需要Info.plist添加文件。但是还是建议跟随时代潮流。

错误信息

原始工程链接

链接: https://download.csdn.net/download/qq_32141141/10988944.

2014-03-31 22:26:05 inspironx 阅读数 5969
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

TexturePacker可以将游戏素材零碎的图片打包在一块,这样更方便读取

而与Unity3d prefabs组合起来使用,使得读取图片达到一个速率更高的层次


TexturePacker:

a.下载最新版的TexturePacker软件

b.打开软件后,点击Add Sprites,添加一些图片

c.然后点击View-textureSettings打开设置面板

d.选择Unity - JSON data(.txt)


e.选择存放目录,回到主界面,点击publish

会在目录下生成两个文件,一个.txt,另一个是.png图片集


在Unity3d中使用:

a.创建一个Empty Object,再在项目视窗中创建一个Material

b.将生成的两个文件加到项目视窗中

c.将Material的Shader选择为Unlit/Transparent Colored

d.用刚生成的.png作为Material的贴图

e.将NGUI的UIAtlas(Script)加载到刚创建的Empty Object上


UIAtlas(Script):中的可选参数


Atlas Type:图片集的类型,Normal,Reference(引用已经做好的图片集)

Material:材质,选择我们刚做好的材质

TP Import:图片集的分割选项

Pixel Size:相素比例


将TP Import中加载生成的.txt后会生成Sprite分割选项,至此,Unity3d与TexurePacker的配合使用完成

然后制作成prefabs,在后续创建的Widget中可以引用到


Unity3d NGUI的使用(四)(TexturePacker与UIAtlas生成prefabs)

2014-05-19 19:54:53 hale445566 阅读数 4821
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

unity里的图片在生成时会压缩成资源文件,有时客户想自己放一些图片用unity显示,就必须加载外部图片。

大体思路:用Application.streamingAssetsPath或Application.dataPath来指定存放图片的相对路径。用DirectoryInfo获得文件夹,遍历后FileInfo获得所有文件,然后判断后缀名是否为图片,是的话用www.texture转为图片并存入集合。


完整代码:

using UnityEngine;
using System.Collections;
using System.IO;
using System.Collections.Generic;
public class LoadFileImage : MonoBehaviour {
    //是否显示图片
    public static bool isShow;
    //图片文件路径集合
    List<string> fileName=new List<string>();
    //图片集合
    List<Texture2D> textureList=new List<Texture2D>();
    public Texture2D close;
    public Texture2D next;
    public Texture2D previous;
    private int mainTexNum;
    private float sW;
    private float sH;

    void Start() {
        sW = Screen.width;
        sH = Screen.height;
        GetAllFile(new DirectoryInfo(UnityEngine.Application.streamingAssetsPath+"/"));
        GetAllTexture();
    }

    void OnGUI() {
        GUI.depth = 0;
        if (!isShow) return;

        GUI.DrawTexture(new Rect(0, 0, sW, sH), textureList[mainTexNum]);

        //下一页
        ChangeTexture(next);
        if (GUI.Button(new Rect(sW / 2 + 20, sH - next.height - 20, next.width, next.height), ""))
        {
            if (mainTexNum == textureList.Count - 1)
                mainTexNum = 0;
            else
                mainTexNum += 1;
        }
        //上一页
        ChangeTexture(previous);
        if (GUI.Button(new Rect(sW / 2 - previous.width - 20, sH - previous.height - 20, previous.width, previous.height), ""))
        {
            if (mainTexNum == 0)
                mainTexNum = textureList.Count - 1;
            else
                mainTexNum -= 1;
        }
        //关闭
        ChangeTexture(close);
            if (GUI.Button(new Rect(sW - close.width - 10, 10, close.width, close.height), ""))
                isShow = false;
    }

    /// <summary>
    /// 根据文件路径加载图片
    /// </summary>
    private void GetAllTexture() {
        foreach (string s in fileName) {
            WWW www = new WWW("file://"+s);
            textureList.Add(www.texture);
        }
    }
    /// <summary>
    /// 获得全部图片文件路径
    /// </summary>
    /// <param name="info">文件夹</param>
    private void GetAllFile(FileSystemInfo info){
         if(!info.Exists) return;
        DirectoryInfo dir=info as DirectoryInfo;
        if(dir==null) return;
        FileSystemInfo[] si=dir.GetFileSystemInfos();
        for (int i = 0; i < si.Length; i++) {
            FileInfo fi = si[i] as FileInfo;
            if (fi != null)
            {
                if (IsImage(fi.Extension))
                    fileName.Add(fi.FullName);
            }
            else
                GetAllFile(si[i]);
        }
        
    }
    /// <summary>
    /// 判断文件是不是图片
    /// </summary>
    /// <param name="name">文件名</param>
    private bool IsImage(string name) {
        string[] imageName = { ".jpg", ".png", ".gif", ".bmp", ".psd", ".tga", ".psd", ".JPG", ".PNG", ".GIF", ".BMP", ".PSD", ".TGA", ".PSD" };
       
        for (int i = 0; i < imageName.Length;i++ )
        {
            if (name.Equals(imageName[i]))
                return true;
        }
        return false;
    }

    /// <summary>
    /// 改变按钮三种状态图片
    /// </summary>
    /// <param name="tex"></param>
    private void ChangeTexture(Texture2D tex)
    {
        GUI.skin.button.normal.background = tex;
        GUI.skin.button.hover.background = tex;
        GUI.skin.button.active.background = tex;
    }
}




2019-09-13 11:42:09 weixin_40377691 阅读数 19
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

3D Game Programming & Design

这是本学期3D游戏编程与设计的第一次作业,内容主要是熟悉Unity3D的基本原理和使用方法,以及C#的编程语法。

1、简答题

1)解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系。

在Unity的官方介绍文档当中,可以找到对Assets的定义如下:

An Asset is a representation of any item you can use in your game or Project. An Asset may come from a file created outside of Unity, such as a 3D Model, an audio file, an image, or any of the other file types that Unity supports. There are also some Asset types that you can create in Unity, such as a ProBuilder Mesh, an Animator Controller, an Audio Mixer, or a Render Texture.

资源是在游戏设计过程中你可以使用的一切物体和属性。Unity中有很多中资源, GameObject、Component理论上也都是资源,但我们通常说的资源指的是:材质、网格、动画和给一个物体插入一段代码等等。

而对GameObjects的定义如下:

GameObjects are the fundamental objects in Unity that represent characters, props and scenery. They do not accomplish much in themselves but they act as containers for Components, which implement the real functionality.
For example, a Light object is created by attaching a Light component to a GameObject.

A GameObject always has a Transform component attached (to represent position and orientation) and it is not possible to remove this. The other components that give the object its functionality can be added from the editor’s Component menu or from a script. There are also many useful pre-constructed objects (primitive shapes, Cameras, etc) available on the GameObject > 3D Object menu, see Primitive Objects.

游戏对象则是游戏当中呢的玩家,物体或者环境等等,它主要是代表一个角色。游戏对象可以由很多的资源组成,比如在meterial可以更改物体的颜色属性,还可以增加代码脚本,增加动画声音等等。

资源可以被多个对象使用,只需要把要用的资源呢拖到对应游戏对象处就行了,资源作为模版,可以实例化游戏中具体的对象。

2)下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)

一个Unity3D 的游戏项目文件夹 assets子目录

一个用Unity3d做的游戏项目中会包含一个Assets文件夹。资源文件夹的内容呈现在项目视图。这里存放着游戏的所有资源,在资源文件夹中,通常包括材质Materials、图片Images、预制件Prefabs、场景Scences、声音Sounds、脚本Scripts等等,根据游戏具体的需求和设计稍有不同,在这些文件夹下可以继续进行划分。

游戏对象一般包括玩家,敌人,环境,摄像机等虚拟父类,对象按照用途进行组织,执行相同操作的对象放在一起。在unity3d的操作界面左边拖动不同的游戏对象,可以调整它们直接的层次关系,使得一个成为另一个的子类或父类,子类会继承父类的属性。

3)编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件。

基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
常用事件包括 OnGUI() OnDisable() OnEnable()

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
	void Awake()
	{
		Debug.Log("Awake");
	}
	// Use this for initialization
	void Start()
	{
		Debug.Log("Start");
	}
	// Update is called once per frame
	void Update()
	{
		Debug.Log("Update");
	}
	void FixedUpdate()
	{
		Debug.Log("FixedUpdate");
	}
	void LateUpdate()
	{
		Debug.Log("LateUpdate");
	}
	void OnGUI()
	{
		Debug.Log("OnGUI");
	}
	void OnDisable()
	{
		Debug.Log("OnDisable");
	}
	void OnEnable()
	{
		Debug.Log("OnEnable");
	}
}

运行的结果和顺序如下:
在这里插入图片描述

4)查找脚本手册,了解 GameObject,Transform,Component 对象

  • 分别翻译官方对三个对象的描述(Description)

GameObject :GameObjects are the fundamental objects in Unity that represent characters, props and scenery. They do not accomplish much in themselves but they act as containers for Components, which implement the real functionality.

翻译:游戏对象是Unity中表示游戏角色,游戏道具和游戏场景的基本对象。它们本身并不能完成很多功能,但是它们充当了那些给予他们实体功能的组件的容器。

Transform :The Transform component determines the Position, Rotation, and Scale of each object in the scene. Every GameObject has a Transform.

翻译:转换决定游戏对象的位置,以及它是如何旋转和缩放的。每个游戏对象都有一个转换。

Component :Components are the nuts & bolts of objects and behaviors in a game. They are the functional pieces of every GameObject.

翻译: 组件是游戏中对象和行为的细节,它是每个游戏对象的功能部分。

  • 描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件.
    本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
    例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。

    • table对象的属性:activeInHierarchy(表示GameObject是否在场景中处于active状态)、activeSelf(GameObject的本地活动状态)、isStatic(仅编辑器API,指定游戏对象是否为静态)、layer(游戏对象所在的图层。图层的范围为[0 … 31])、scene(游戏对象所属的场景)、tag(游戏对象的标签)、transform(附加到这个GameObject的转换)
    • Table的Transform属性:Position、Rotation、Scale
    • Table的部件:Mesh Filter、Box Collider、Mesh Renderer
  • 用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
    在这里插入图片描述

5)整理相关学习资料,编写简单代码验证以下技术的实现:

  • 查找对象

    • 通过名字查找:
      public static GameObject Find(string name);
      This function only returns active GameObjects. If no GameObject with name can be found, null is returned. If name contains a ‘/’ character, it traverses the hierarchy like a path name.
    • 通过标签查找单个对象:
      public static GameObject FindWithTag(string tag);
      Returns one active GameObject tagged tag. Returns null if no GameObject was found.
      Tags must be declared in the tag manager before using them. A UnityException will be thrown if the tag does not exist or an empty string or null is passed as the tag.
    • 通过标签查找多个对象:
      public static GameObject[] FindGameObjectsWithTag(string tag);
      Returns a list of active GameObjects tagged tag. Returns empty array if no GameObject was found. Tags must be declared in the tag manager before using them. A UnityException will be thrown if the tag does not exist or an empty string or null is passed as the tag.
  • 添加子对象
    public static GameObject CreatePrimitive(PrimitiveType type);
    Creates a game object with a primitive mesh renderer and appropriate collider.

  • 遍历对象树

foreach (Transform child in transform) {  
    Debug.Log(child.gameObject.name);  
} 
  • 清除所有子对象
foreach (Transform child in transform) {  
    Destroy(child.gameObject);  
} 

6)资源预设(Prefabs)与 对象克隆 (clone)

  • 预设(Prefabs)有什么好处?
    预设体可以理解成是模版,它是可以反复利用的游戏对象,储存了完整储存了对象的组件和属性。预设使得修改的复杂度降低,,在后续创建相似的游戏对象的时候更方便,只需要修改预设即可,所有通过预设实例化的对象都会做出相应变化。
  • 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
    修改预设会使通过预设实例化的所有对象都做出相应变化,而对象克隆出来后修改其中任何一个不会对其他的产生影响。
  • 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
    创建完一个table实例后,将该对象拖到Assets池里就变成了perfabs预设体了。
 void Start () {  
        GameObject anotherTable = (GameObject)Instantiate(table.gameObject);  
    }

2、 编程实践,小游戏

  • 游戏内容: 井字棋 或 贷款计算器 或 简单计算器 等等
  • 技术限制: 仅允许使用 IMGUI 构建 UI
  • 作业目的:
    了解 OnGUI() 事件,提升 debug 能力
    提升阅读 API 文档能力

1)实验前基础知识储备

理解OnGUI() 事件

“Immediate Mode” GUI系统(也称为IMGUI)是Unity的一个独立于图形化视图的代码特性系统,完全通过代码来实现基于游戏对象的UI设计和其他功能的设计。IMGUI是一个代码驱动的GUI系统,主要用于作为程序员的工具。它是由对任何实现它的脚本的OnGUI函数的调用驱动的。

要创建IMGUI元素,必须编写代码进入一个名为OnGUI的特殊函数。代码需要在每一帧显示这个接口并绘制到屏幕上。除了你的代码附载的那个游戏对象以外,没有别的一直存在的或是和代码所依附的对象有继承关系的的对象了。

IMGUI允许您使用代码创建各种各样的功能GUI。与其创建gameobject(游戏对象),手动定位它们,然后编写一个脚本来处理其功能,您可以用几行代码立即完成所有事情。代码生成GUI控件,这些控件通过一个函数调用来绘制和处理。

基本函数操作

  • Label
/* GUI.Label example */

using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour 
{
                   
   void OnGUI () 
   {
       GUI.Label (new Rect (25, 25, 100, 30), "Label");
   }

}

The Label is non-interactive. It is for display only. It cannot be clicked or otherwise moved. It is best for displaying information only.

  • Button
/* GUI.Button example */

using UnityEngine;
using System.Collections;

public class GUITest : MonoBehaviour 
{
                   
   void OnGUI () 
   {
       if (GUI.Button (new Rect (25, 25, 100, 30), "Button")) 
       {
           // This code is executed when the Button is clicked
       }
   }

}

In UnityGUI, Buttons will return true when they are clicked. To execute some code when a Button is clicked, you wrap the the GUI.Button function in an if statement. Inside the if statement is the code that will be executed when the Button is clicked.

2)实验代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Chess : MonoBehaviour
{
   private int[,] chessBoard = new int[3, 3];//初始化井字棋棋盘
   private int turn = 1;

   // Use this for initialization
   void Start()
   {
       Reset();
   }

   // Update is called once per frame
   void Update()
   {

   }

   void Reset()
   {
       turn = 1;
       for (int i = 0; i < 3; i++)
       {
           for (int j = 0; j < 3; j++)
           {
               chessBoard[i, j] = 0;
           }
       }
   }

   void OnGUI()
   {
       // GUIStyle design.
       GUIStyle Style = new GUIStyle();
       Style.normal.background = null;
       Style.normal.textColor = new Color(0, 5, 1);
       Style.fontSize = 35;
       Style.fontStyle = FontStyle.Bold;
       
       //Type (Position, Content)
       //Rect() defines four properties: left-most position, top-most position, total width, total height.
       //You can use the Screen.width and Screen.height properties to get the total dimensions of the screen space available in the player.
       GUI.Button(new Rect(Screen.width / 2 - 75, Screen.height / 2 - 250, 150, 40), "Tic Tac Toe");

       if (GUI.Button(new Rect(Screen.width / 2 - 75, Screen.height / 2 -200, 150, 50), "RESET GAME"))//displays a Box Control with the header text “RESET GAME”.
       {
           Reset();
       }

       //First judge the result.
       int State = check();
       //The second argument for a GUI Control is the actual content to be displayed with the Control. Most often you will want to display some text or an image on your Control.
       if (State == 2)
       {
           GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 +50, 50, 50), "X Win!", Style);
       }
       else if (State == 1)
       {
           GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 +50, 50, 50), "O Win!", Style);
       }
       else if (State == 0)
       {
           GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 +50, 50, 50), "Equally", Style);
       }

       //The Game is not over,continue.
       for (int i = 0; i < 3; i++)
       {
           for (int j = 0; j < 3; j++)
           {
               //If the number of the chessboard is 1, it represents O, else 2 represents X.
               if (chessBoard[i, j] == 1)
               {
                   GUI.Button(new Rect(Screen.width / 2 - 75 + 50 * i, Screen.height / 2 - 130 + 50 * j, 50, 50), "O");
               }
               else if (chessBoard[i, j] == 2)
               {
                   GUI.Button(new Rect(Screen.width / 2 - 75 + 50 * i, Screen.height / 2 - 130 + 50 * j, 50, 50), "X");
               }

               //Click operation.
               if (GUI.Button(new Rect(Screen.width / 2 - 75 +50 * i, Screen.height / 2 - 130 + 50 * j, 50, 50), ""))
               {
                   if (State == 3)
                   {
                       if (turn == 1)
                       {
                           chessBoard[i, j] = 1;
                       }
                       else if (turn == -1)
                       {
                           chessBoard[i, j] = 2;
                       }
                       turn = -turn;
                   }
               }
           }
       }
   }

   int check()
   {
       //Check the rows.
       for (int i = 0; i < 3; i++)
       {
           if (chessBoard[i, 0] == chessBoard[i, 1] && chessBoard[i, 0] == chessBoard[i, 2] && chessBoard[i, 0] != 0)
           {
               return chessBoard[i, 0]; //1
           }
       }

       //Check the columns.
       for (int j = 0; j < 3; j++)
       {
           if (chessBoard[0, j] == chessBoard[1, j] && chessBoard[0, j] == chessBoard[2, j] && chessBoard[0, j] != 0)
           {
               return chessBoard[0, j]; //2
           }
       }

       //Check the diagonals.
       if (chessBoard[0, 0] == chessBoard[1, 1] && chessBoard[0, 0] == chessBoard[2, 2] && chessBoard[0, 0] != 0) return chessBoard[0, 0];

       if (chessBoard[0, 2] == chessBoard[1, 1] && chessBoard[0, 2] == chessBoard[2, 0] && chessBoard[0, 2] != 0) return chessBoard[0, 2];

       //Cteate count to find out who win the game.
       //If count == 9, then nobody win the game, the game go equally.
       int count = 0;
       for (int i = 0; i < 3; i++)
       {
           for (int j = 0; j < 3; j++)
           {
               if (chessBoard[i, j] != 0)// some player put chess on that button.
               {
                   count++;
               }
           }
       }
       if (count == 9)
       {
           return 0;
       }
       return 3; //3
   }

}

3)实验结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
游戏实操视频

Github代码传送门

3、思考题(选做)

1)微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。

为什么是“模板方法”模式而不是“策略模式”呢?

模版方法模式:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义改算法的某些特定步骤。

策略模式:

策略模式它定义了算法家族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化,不会影响到使用算法的客户。

根据上述对模版方法模式和策略模式的定义我们可以看出,这里微软XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们的核心思想不在于分离用户和算法,而是模版方法模式的核心——定义好算法的框架,用子类去处理不同的方法实现,微软 XNA 引擎的 Game 对象使用虚方法让子类完成游戏循环的细节。

2)将游戏对象组成树型结构,每个节点都是游戏对象(或数)。

  • 尝试解释组合模式(Composite Pattern / 一种设计模式)。

组合模式:

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

  • 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?

父对象:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
   
public class father : MonoBehaviour {
   void Start () {
   	this.BroadcastMessage("test");
   }
}

子对象:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class child : MonoBehaviour {
   void test(){
   	Debug.Log ("child");
   }
}

将两个脚本分别拖动到父对象和子对象的game object上运行。BroadcastMessage的伪代码为:

public void BroadcastMessage(string bm){
    for each children
        call its method named bm
}

3)一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component)。

  • 这是什么设计模式?
    装饰器模式(Decorator Pattern)。

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

  • 为什么不用继承设计特殊的游戏对象?

因为特殊的游戏对象意味着具有很强的灵活性。它需要在保证完成基础对象完整性的前提下,提供额外的功能,动态增加功能,而这恰好符合装饰器模式的定义。如果使用继承的话,则会产生大量的类,不便于管理,代码的可移植性也更差。

unity3d模板--

阅读数 152

没有更多推荐了,返回首页