Prefab资源和Random类 .

本文介绍Unity3D中的预制(Prefab)资源及Random类的使用技巧,包括实例化函数、继承特性、恢复链接等,通过示例演示如何在游戏开发中利用随机数生成奖励。
Unity3D是由Unity Technologies开发的一个专业游戏引擎,虽然说不及UDK所创造的效果,但它高效率的开发、对多个平台的支持使在国内外具有很高的人气(七月中旬测试的新仙剑奇侠传online便是使用U3D)
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
 
Unity比较容易上手,关于具体的介绍这里不多浪费笔墨,可以去官网得到更多的相关信息(http://unity3d.com/),今天主要是讨论一下Unity中的一种资源——预制(Prefab)以及Random类。

【PrefabAsset】
预制属于unity的一种资源,通过使用预制,只需调用实例化函数Instantiate就可以创建实例物体,极为方便;当你想要创建一大群类型相似(勉强描述,比如敌人)的物体,使用预制可以方便的管理,比如你想添加一个组件,只需在预制源添加即可,这样每个实例都会自动添加这个组件,而无需一个个添加。
你可以在Project中右键创建它(或是Asset->Create->Prefab),此时它是一个空的预制,你需要将Hierarchy视图中的一个物体拖拽到新预制中,可以看到该物体变成了蓝色。现在,该物体及其子对象就已经复制到了预制数据中,该物体称作该预制的一个实例(instance)。你可以在运行时通过脚本调用Instantiate();函数实例化预制
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
 
【继承】
当物体被实例化后,他可以拥有与其他实例不同的数据,使你的实例变得独一无二。当你改变实例的数据(如变量)时,它将以粗体显示,表示这是可重写的变量,但只影响该物体,其他的物体不会受到影响。
如果你希望应用到所有实例,点击Apply;在没有应用之前使用Revert可以恢复被改变的值
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
 
【恢复链接】
当你添加或删除一个组件或者子对象时,可能会破坏单个实例与预制源之间的链接,要恢复链接:
           Reconnect——丢弃该实例与预制源的差别,恢复原样
           Apply——将复制这些差别到所有其他的实例

【实例化函数】(//注:此处用的是js函数原型)
static function Instantiate (original : Object, position : Vector3rotation : Quaternion) : Object
这个函数实例化一个物体并返回,
static function Instantiate (original : Object) : Object
这个函数将克隆一个物体并返回,与Duplicate命令是相同的
上述两个函数实质上是克隆物体或者使一个预制产生实例,一般是事先定义一个GameObject(public)或者是Transform(或者其它组件),然后original则是这个定义的变量。
*注意,如果你定义了GameObject,你既可以从ProjectHierarchy视图拖拽到变量上为其赋值(要锁定Inspector),也可以从变量右边的圆圈打开选择,但是如果你定义的是组件类型的变量,那么如果你用第二种办法会发现Asset选栏中是空的,你只能使用拖拽的办法。

【RandomClass】
Random类用于产生随机数据,具体的类变量和函数这里不一一列举,不过需要注意的一点是,Range类函数经常会用到,如果你用的是static function Range (min : int, max : int) : int那么它返回的数可能会返回min,但是永远不会反回max,也就是说,如果你需要从1-10返回任意一个数,你想他有一定概率返回10,那么max就要写11,而不是10。
如果你用的是static function Range (min : float, max : float) : float,那么返回的范围将包含两个极端值。

游戏中常常需要用到随机的策略,比如说你希望玩家在战斗结束后抽选胜利品,那么你可以这么做:摆出9张牌,让玩家自由选择一张,然后随机得到一个奖励,这里所谓的“随机”就是说不是你一开始就设计好了,而是当他选择时让程序自己抽取计算,从奖励库中随即取出一个奖励,那么这里就需要用到Random类,Random类的功能:
*从数组内抽取一个随机元素
*用不同概率选中数据
*随机排序
*随机放置物体,并排除重复
*...............等等,它还可以结合Instantiate函数,实例化物体,并放置在随机的位置,或是赋予其随机的数据。
*【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋

【简单例子】
【实现效果】:
刚才随机中讨论到一个有趣的例子,在屏幕上放置9个框子,让玩家自由选择一个,然后输出奖励信息
p.s.:关于这一类文章如果举例子,例子内容尽可能简单或者说是简陋,不在资源素材上下过多功夫

【资源】:
一、正常情况的框子 二、鼠标点下时的框子
p.s.:导入时选择Texture Type选择GUI,你无须选择Advanced一个个参数调整。

【流程】:
1.GameObject->Create Other->GUI Texture创建一个GUI纹理,放置纹理。
2.GameObject->Create Other->GUI Text创建一个GUIText,用于输出奖励信息。
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
 2.Project视图下,右键创建一个Prefab,将刚创建的物体拖到Prefab中,更改其Texture属性,Pixel Inset下的HeightWidth属性。
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
4.从Prefab创建8个物体,并更改他们的Pixel Inset的X、Y属性,摆好位置 。
p.s.:这里用不用Prefab无所谓,你也可以直接复制9个GUITexture物体。
5.选择Main Camera,Background下改成白色(框子我没有做alpha通道)。
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
6.因为背景改白色了,刚创建的GUIText物体默认情况下是白色字体,现在要更换一种颜色。
7.创建一个C#脚本,要改颜色,你可以创建一个材质,并且指定颜色,然后赋予到Material属性栏,或者在Awake函数下调用:guiText.material.color = Color.red;
8.现在,要判断鼠标是否放在框子上面,如果放在框子上,更换一个纹理,当鼠标按下时,输出奖励信息
我的思路是创建一个脚本,赋予到Prefab上,使用OnMouse系列函数响应
输出信息使用随机判断,使用Random类Range函数,从1-9返回一个int值,然后判断这个值是多少,输出信息,也就是说获得每种奖励的概率是九分之一,当然你可能希望获得低奖励几率更高些。
代码如下;
using UnityEngine;
using System.Collections;

public class Random_demo : MonoBehaviour {
public GameObject guiText_pt;
public Texture2D normal_tex;
public Texture2D down_tex;
string print_str;
void OnMouseEnter () {
guiTexture.texture = down_tex;
}
    void OnMouseUp(){
int rnd = Random.Range(1,10);
switch(rnd)
{
case 1: 
       print_str = "EXP+100";
           break;
   case 2: 
       print_str = "EXP+200";
           break;
   case 3: 
       print_str = "EXP+400";
           break;
   case 4: 
       print_str = "EXP+800";
           break;
   case 5: 
       print_str = "EXP+1600";
           break;
   case 6: 
       print_str = "EXP+3200";
           break;
   case 7:  
       print_str = "EXP+6400";
           break;
   case 8: 
       print_str = "EXP+12800";
           break;
   case 9: 
       print_str = "EXP+25600";
           break;
}
guiText_pt.guiText.text = print_str;
}
void OnMouseExit(){
guiTexture.texture = normal_tex;
}
}
*或许你可以用一个数组,抽取数组中的元素更好,这里就非常笼统的这么写了。
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
【最终效果】
【U3D】Prefab资源和Random类 - Green_Star - GreenStar的星际小屋
*这是我发布后的效果,关于发布这方面以后会谈到,你只需简单地选择发布选项,加入你所需要的场景即可。
// SkillManager.ts import { _decorator, Component, Node, Prefab, Vec3, v3, instantiate, Tween, tween, find } from 'cc'; const { ccclass, property } = _decorator; import { GameManager } from './GameManager'; // 导入GameManager import { MonsterController } from './MonsterController'; // 导入MonsterController @ccclass('SkillManager') export class SkillManager extends Component { @property({type: [Prefab]}) private skillsPrefabs: Prefab[] = []; // 技能特效节点预制体数组 // 添加对GameManager的引用 @property({type: Node}) private gameManagerNode: Node; // 挂载GameManager的节点 private gameManager: GameManager | null = null; // 存储当前激活的技能实例 private activeWanJianJueSkills: Node[] = []; //存储万剑诀实例 private activeDuJieShuSkills: Node[] = []; // 存储渡劫术实例 // 英雄节点引用 private heroNode: Node | null = null; //技能卡片界面节点 private skillCards:Node | null = null; protected onLoad(): void { // 获取GameManager组件 if (this.gameManagerNode) { this.gameManager = this.gameManagerNode.getComponent(GameManager); // 获取英雄节点 this.heroNode = this.gameManager.HeroNode; } //获得技能卡片界面节点 this.skillCards = find('Canvas/UI/SkillCards') } //按键启动万剑诀 public buttenWanJianJue(){ // 获取最近的怪兽作为万剑诀落点 let nearestMonster = this.getNearestMonsterForSkill(); if (nearestMonster) { // 使用怪兽位置作为落点 const position = new Vec3(470, -185, 0); position.add(nearestMonster.position); // 在怪兽位置基础上调整偏移 this.startWanJianJue(position); } else { console.warn("没有找到有效的怪兽作为万剑诀落点"); } } /** * 获取最近的怪兽节点作为技能目标 * @returns 最近的怪兽节点或null */ private getNearestMonsterForSkill(): Node | null { if (!this.gameManager) { console.warn("GameManager未设置"); return null; } return this.gameManager.getNearestAliveMonster(); } /** * 启动万剑诀技能 * @param targetPosition 打击位置 */ private startWanJianJue(targetPosition: Vec3): void { // 1. 找到万剑诀预制体 const wanJianJuePrefab = this.skillsPrefabs.find(prefab => prefab.name === "WanJianJue"); if (!wanJianJuePrefab) { console.error("万剑诀预制体未找到!"); return; } // 2. 清空之前的技能实例 this.clearActiveSkills(); // 3. 创建10个万剑诀实例 for (let i = 0; i < 10; i++) { this.createWanJianJueInstance(wanJianJuePrefab, targetPosition); } // 4. 0.6秒后检测伤害 this.scheduleOnce(() => { this.detectSkillDamage(targetPosition.add(new Vec3(-470, +185, 0))); }, 0.6); // 5. 0.8秒后删除所有技能实例 this.scheduleOnce(() => this.clearActiveSkills(), 0.8); } /** * 检测技能伤害 * @param targetPosition 技能中心位置 */ private detectSkillDamage(targetPosition: Vec3): void { if (!this.gameManager) { console.warn("GameManager未设置,无法检测技能伤害"); return; } // 获取所有怪物 const monsters = this.gameManager.getAllMonsters(); const damageRadius = 30; // 伤害半径30像素 const damageAmount = 40; // 伤害值40点 for (const monster of monsters) { if (!monster || !monster.isValid) continue; const controller = monster.getComponent(MonsterController); if (!controller || controller.isDie) continue; // 计算距离 const distance = Vec3.distance(monster.position, targetPosition); if (distance <= damageRadius) { // 对怪物造成伤害 controller.takeDamage(damageAmount); } } } /** * 创建单个万剑诀实例 * @param prefab 预制体 * @param targetPosition 目标位置 */ private createWanJianJueInstance(prefab: Prefab, targetPosition: Vec3): void { // 创建实例 const instance = instantiate(prefab); // 确保英雄节点有效 if (!this.heroNode || !this.heroNode.isValid) { console.warn("英雄节点无效,无法创建万剑诀实例"); instance.destroy(); return; } // 获取英雄位置 const heroPos = this.heroNode.position.clone(); // 位置生成参数 const minX = heroPos.x - 400 + 470; const maxX = heroPos.x + 400 + 470; const minY = -350 - 185; const maxY = -50 -185; const minDistance = 80; // 距离英雄的最小距离 let position: Vec3; let attempts = 0; const maxAttempts = 100; // 最大尝试次数 // 尝试找到一个满足条件的位置 do { attempts++; // 在指定范围内生成随机位置 const randomX = Math.random() * (maxX - minX) + minX; const randomY = Math.random() * (maxY - minY) + minY; position = v3(randomX, randomY, heroPos.z); // 检查是否满足距离要求 const distanceToHero = Vec3.distance(position, heroPos); // 如果满足距离要求或达到最大尝试次数 if (distanceToHero >= minDistance || attempts >= maxAttempts) { break; } } while (true); // 设置位置 instance.setPosition(position); // 添加到场景 this.heroNode.parent.addChild(instance); // 添加到激活技能列表 this.activeWanJianJueSkills.push(instance); } /** * 清空所有激活的技能实例 */ private clearActiveSkills(): void { for (const skill of this.activeWanJianJueSkills) { if (skill.isValid) { // 添加消失动画 skill.destroy() } } this.activeWanJianJueSkills = []; } /** * 启动横扫千军技能 */ public buttenHengSaoQianJun() { // 在英雄位置创建横扫千军特效 this.startHengSaoQianJun(); } /** * 创建横扫千军技能 */ private startHengSaoQianJun(): void { // 1. 找到横扫千军预制体 const hengSaoQianJunPrefab = this.skillsPrefabs.find(prefab => prefab.name === "HengSaoQianJun1"); if (!hengSaoQianJunPrefab) { console.error("横扫千军预制体未找到!"); return; } // 确保英雄节点有效 if (!this.heroNode || !this.heroNode.isValid) { console.warn("英雄节点无效,无法创建横扫千军特效"); return; } // 创建实例 const instance = instantiate(hengSaoQianJunPrefab); // 获取英雄朝向(Scale.x为正表示朝左,为负表示朝右) const heroScaleX = this.heroNode.scale.x; const isHeroFacingLeft = heroScaleX > 0; // 设置偏移量 - 根据英雄朝向调整X方向 const offsetX = isHeroFacingLeft ? 364 : -364; const offset = new Vec3(offsetX, 25, 0); // 计算最终位置 const heroPos = this.heroNode.position.clone(); const position = new Vec3( heroPos.x + offset.x, heroPos.y + offset.y, heroPos.z + offset.z ); instance.setPosition(position); // 调整技能朝向 - 与英雄朝向一致 const currentScale = instance.scale; if (isHeroFacingLeft) { // 英雄朝左,技能朝左(Scale.x为正) instance.setScale(new Vec3(Math.abs(currentScale.x), currentScale.y, currentScale.z)); } else { // 英雄朝右,技能朝右(Scale.x为负) instance.setScale(new Vec3(-Math.abs(currentScale.x), currentScale.y, currentScale.z)); } // 添加到场景 this.node.addChild(instance); // 添加到激活技能列表 this.activeWanJianJueSkills.push(instance); // 添加伤害检测 this.scheduleOnce(() => { this.detectHengSaoDamage(position); }, 0.7); // 0.7秒后检测伤害 // 1.7秒后删除技能实例 this.scheduleOnce(() => { if (instance.isValid) { instance.destroy(); // 从激活列表中移除 const index = this.activeWanJianJueSkills.indexOf(instance); if (index !== -1) { this.activeWanJianJueSkills.splice(index, 1); } } }, 1.6667); } /** * 检测横扫千军伤害 * @param position 技能位置 */ private detectHengSaoDamage(position: Vec3): void { if (!this.gameManager) { console.warn("GameManager未设置,无法检测技能伤害"); return; } // 获取英雄朝向(Scale.x为正表示朝左,为负表示朝右) const heroScaleX = this.heroNode.scale.x; const isHeroFacingLeft = heroScaleX > 0; // 获取英雄位置 const heroPos = this.heroNode.position.clone(); // 定义伤害区域参数 const damageLength = 400; // 伤害区域长度(像素) const damageWidth = 200; // 伤害区域宽度(像素) const damageAmount = 80; // 伤害值80点 // 计算伤害区域边界 let minX: number, maxX: number; let minY: number, maxY: number; if (isHeroFacingLeft) { // 英雄朝左,伤害区域向左延伸 minX = heroPos.x - damageLength; maxX = heroPos.x; } else { // 英雄朝右,伤害区域向右延伸 minX = heroPos.x; maxX = heroPos.x + damageLength; } // Y轴范围(以英雄位置为中心) minY = heroPos.y - damageWidth / 2; maxY = heroPos.y + damageWidth / 2; // 获取所有怪物 const monsters = this.gameManager.getAllMonsters(); for (const monster of monsters) { if (!monster || !monster.isValid) continue; const controller = monster.getComponent(MonsterController); if (!controller || controller.isDie) continue; // 获取怪物位置 const monsterPos = monster.position; // 检查怪物是否在伤害区域内 const inXRange = monsterPos.x >= minX && monsterPos.x <= maxX; const inYRange = monsterPos.y >= minY && monsterPos.y <= maxY; if (inXRange && inYRange) { // 对怪物造成伤害 controller.takeDamage(damageAmount); } } } // 按键启动渡劫术 public buttonDuJieShu() { // 获取所有存活的怪兽 const allMonsters = this.getAllAliveMonsters(); if (allMonsters.length > 0) { this.startDuJieShu(allMonsters); } else { console.warn("没有存活的怪兽作为渡劫术目标"); } } /** * 获取所有存活的怪兽 * @returns 存活怪兽节点数组 */ private getAllAliveMonsters(): Node[] { if (!this.gameManager) { console.warn("GameManager未设置"); return []; } return this.gameManager.getAllMonsters(); } /** * 启动渡劫术技能 * @param targetMonsters 目标怪物节点数组 */ private startDuJieShu(targetMonsters: Node[]): void { // 1. 找到渡劫术预制体 const duJieShuPrefab = this.skillsPrefabs.find(prefab => prefab.name === "DuJieShu-1"); if (!duJieShuPrefab) { console.error("渡劫术预制体未找到!"); return; } // 2. 为每个怪物创建渡劫术实例 for (const monster of targetMonsters) { if (!monster || !monster.isValid) continue; const controller = monster.getComponent(MonsterController); if (!controller || controller.isDie) continue; // 创建渡劫术实例 const instance = instantiate(duJieShuPrefab); // 设置位置为怪物位置 instance.setPosition(monster.position.clone()); // 添加到场景 this.heroNode.parent.addChild(instance); // 添加到激活技能列表 this.activeDuJieShuSkills.push(instance); // 3. 立即对目标怪物造成伤害 this.applyDuJieShuDamage(monster); // 4. 0.4667秒后删除技能实例 this.scheduleOnce(() => { if (instance.isValid) { instance.destroy(); // 从激活列表中移除 const index = this.activeDuJieShuSkills.indexOf(instance); if (index !== -1) { this.activeDuJieShuSkills.splice(index, 1); } } }, 0.4667); } } /** * 应用渡劫术伤害 * @param targetMonster 目标怪物节点 */ private applyDuJieShuDamage(targetMonster: Node): void { if (!targetMonster || !targetMonster.isValid) return; const controller = targetMonster.getComponent(MonsterController); if (!controller || controller.isDie) return; // 直接对怪物造成20点伤害 controller.takeDamage(10); console.log(`渡劫术对 ${targetMonster.name} 造成20点伤害!`); } // 组件销毁时清理资源 protected onDestroy(): void { this.clearActiveSkills(); } }修改代码现在为人物加上技能属性, 用外部调用buttenWanJianJue(),buttenHengSaoQianJun(),buttonDuJieShu()这个按键方法,让英雄获得相应的技能。现在按下技能键以后不会立刻发动技能,而是等英雄在场景中与怪兽相遇战斗时再发动技能,如果获得相应技能后发现怪物后每4到5秒发动一次万剑诀;发现怪物后,且与怪物距离在400以内,每1到2秒发动一次渡劫术;英雄处于攻击状态时每3到4秒发动一次横扫千军(平时英雄处于其他状态时不发动)。
07-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值