#cocos creator 3D
export class GameMgr extends Component {
@property({ type: NodePos, tooltip: '箭' })
NodePos_arrow: NodePos = null;
}
builitin-standard
set pass call
(性能优化很重要的一个点)onLoad
方法–》都调用一遍.onStart
只调用一次onLoad
–》.onStart
两个初始化入口// cc.Component --> 组件类的基类
// cc.Node ---> 节点类
// cc.Vec3 ---> 三维向量类
import { _decorator, Component, Node, Vec3 } from "cc";
// 装饰器:ccclass,property
// 装饰器是给编辑装载脚本代码的时候来读取的
// 装饰器:ccclass 是指定一个类是一个组件类
// 装饰器:property指定一个成员变量为属性成员,编辑器读到这个装饰器,会把这个数据成员,作为属性绑定到编辑器
const { ccclass, property } = _decorator;
// component导入进来的组件类的基类
// export 导出这个类 import {game_mgr} from "./game_mgr"
// 装饰器@开头的;
@ccclass("game_mgr") // 编辑器识别我们这个脚本后,会把他当成组件类
export class game_mgr extends Component {
// 权限 名称:类型 = 默认值
//普通的基本数据类型:boolean,number
@property
private is_debug: boolean = false;
@property
private speed: number = 200;
// 复杂数据类型
@property(Node)
private test_node: Node = null;
@property([Node])
private test_array: Array<Node> = [];
start () {
console.log("start")
}
update (deltaTime: number) {
var s = deltaTime * 5;
this.node.translate(cc.v3(0,0,-s))
}
}
var v:Vec3 = cc.v3(3,4,5);
var v1 = new Vec3(0,0,0);
var v3 = new Vec3(v);
UNIT_X: (1, 0, 0), UNIT_Y(0, 1, 0), UNIT_Y(0, 0, 1);ZERO: (0, 0, 0), ONE: (1, 1, 1), NEG_ONE (-1, -1, -1);
var v5 = Vec3.len(v4);
==>v4.length()
v4.add(Vec3.UNIT_Y);
结果是v4Vec3.add(v3,Vec3.ONE,Vec3.NEG_ONE)
结果在v3中var v9 = Vec3.distance(v1,v4);
lerp
范围是(0,1)
V = A + T * (B-A) T(0, 1);
// 线性插值 V = A + T * (B-A) T(0, 1);
// v3 = v1 + 0.3 * (v4 - v1)
Vec3.lerp(v3, v1, v4, 0.3);
console.log(v3);
// v1 = v1 + 0.3 * (v4 - v1);
v1.lerp(v4, 0.3);
console.log(v1);
+ 两个向量点乘dot,原理,
- A(x1,y1,z1),B(x2,y2,z2)
- A*B = (x1*x2+y1*y2+z1*z2)
- A*B = |A|+|B|+cos(夹角)
+ 夹角:两个向量之间较小的角度【0,180】
+ 求夹角:|A|*|B|*cos(夹角) = (x1*x2+y1*y2+z1*z2)--->cos(夹角) = ((x1*x2+y1*y2+z1*z2)/(|A|*|B|))
// 点乘 (x1*x2 + y1 * y2 + z1 * z2)
console.log(Vec3.dot(v1, v4)); // 静态方法;
console.log(v1.dot(v4)); // 成员方法
+ 叉积(cross):法相向量
- 返回一个向量
// 叉积----》(法线向量)
console.log(Vec3.cross(v3, v1, v4)); // v3 = v1 x v4;
// v1.cross(v4); // v1 = v1 x v4;
// console.log(v1);
v4.cross(v1); // v4 = v4 x v1; // 相反的;
console.log(v4);
+ 两个向量单位化:normalize
// (x, y, z) ---> 单位向量, 方向相同, 向量长度的为一;
Vec3.normalize(v3, v4); // v3 = v4的单位向量;
v4.normalize(); // v4 = v4的单位向量;
console.log(v3, v4, v4.length());
(v*dir.x/len,v*dir.y/len,v*dir.z/len)
update(dt: number) {
var speed: number = 5;
this.node.translate(cc.v3(0, 0, -speed * dt), Node.NodeSpace.LOCAL);
// this.node.translate(cc.v3(0, 0, -speed * dt), Node.NodeSpace.WORLD); 记录一下;有问题
var pos: Vec3 = this.node.worldPosition;
pos.z -= (speed *dt)
this.node.worldPosition = pos;
}
// 缩放, 只读, get新new Vec3内存,set;
this.node.setScale(2, 2, 2);
var scale: Vec3 = this.node.scale; // 同一个内存;
scale = this.node.getScale(); // 新建一个内存;
scale = this.node.worldScale;
直观的旋转,任何3D物体,可以把物体旋转某个角度,分别绕x,y,z分别旋转,然后得到一个朝向
缺点:不能直观的表示旋转, 程序一般不直接使用, 引擎内部用四元素计算旋转;
优点:方便参与矩阵计算,没有万向节锁
四元数和欧拉旋转互换:Quat(x,y,z)
.静态方法、成员方法
【4】3D物体旋转控制:
eulerAngles
: 欧拉角;表示本地旋转setRotationFromEuler
: 使用欧拉角来设置物体的本地旋转(x,y,z),绕x轴旋转多少度,绕y轴,z轴多少度。setWorldRotationFromEuler
使用世界旋转(x,y,z) Y-Z-Xthis.node.lookAt(cc.v3(0,10,0));
设置node朝向y轴10方向四元数插值:
slerp
。一般使用,平滑过渡我们的旋转[0,1]—》t—>中间的某个状态—》详细的讲解lerp:A+t*(B-A)
;cc.CameraComponent
:ClearFlags
: 清理屏幕
dont_clear
不清空.原来有什么就是什么;depth_only
只清空深度solid_color
清空颜色、深度与模板缓冲;skyBox
启用天空盒,只清空深度color
: 清理屏幕的颜色;priority
: 相机的渲染优先级,值越小越优先渲染depth
:清空为指定的深度FOV
: 相机的视角大小,near
: 相机的近裁剪距离,应在可接受范围内尽量取最大far
: 相机的远裁剪距离,应在可接受范围内尽量取最小OrthoHeight
正交模式下的视角Projection
相机投影模式。分为 透视投影(PERSPECTIVE) 和 正交投影(ORTHO)Rect
: 此相机最终渲染到屏幕上的视口位置和大小Stencil
清空为指定的模板缓冲TargetTexture
: 指定此相机的渲染输出目标贴图,默认为空,直接渲染到屏幕Visibility
:可见性掩码,声明在当前相机中可见的节点层级集合。material
】Effect
->选择builtin-unlit
(高效的3Dshader)[USE texture]
MainTexture
中添加贴图Materials
添加刚刚做好的贴图material
cc.SkeIetaIAnimationComponent
clips
: 是当前这个组件带了哪些动画剪辑(attack , clip , run, idle)
—>AnimationClip
DefauItCIip
默认的动画剪辑Play on Load
:是否在装载的时候就开始播放,勾选上一一》播放默认的anim clip;play
: 立即切换,播放我们的动画,如果没有给动画的名字—》默认的动画,clips
: 获取我们动画组件上面的,所有的动画剪辑—> cc.AnimationClip数组;defaultClip
: 默认的动画剪辑,crossFade
: 切换动画加上了平滑过渡一》自然;stop
:停止播放动画pause
:暂停播放;import { _decorator, Component, Node, SkeletalAnimationComponent } from "cc";
const { ccclass, property } = _decorator;
@ccclass("aniPlay")
export class aniPlay extends Component {
private anim: SkeletalAnimationComponent = null;
onLoad(){
this.anim = this.node.getComponent(SkeletalAnimationComponent);
}
start () {
this.anim.play('Run');
var that = this;
this.scheduleOnce(function(){
that.anim.crossFade('Die')
},5)
}
}
import {
_decorator,
Component,
Node,
AnimationComponent
} from "cc";
private anim: AnimationComponent = null;
start() {
this.anim = this.node.getComponent(AnimationComponent);
}
this.anim.play('001_flyjian');
flip UV
翻转USE_ALBEDO_MAP
是否使用漫反射贴图?
USE_NORMAL_MAP
是否使用法线贴图?没增加三角形情况下增加更多细节builitin-unlit
最高效的3D shader, 效果最少把一个3D模型最基本的显示出来—>unlinght不受光照影响;
USE VERTEX COLOR
USE ALBEDO MAP
cubemap
cubemap
—导入资源cubemap
导入SWS 路径编辑插件
Waypoint Manager
节点Waypoint Manager
节点—》右边创建名称Enter Path Name
—》一般选标准—》Start Pathbycw_roaddata_export.unitypackage
/../
就会生成一个Waypoint Manager.ts
文件Waypoint Manager.ts
–》WaypointManager.ts
示例:F:\cocos3D\009_test\assets\scenes\012routePoint
重点
: cocos中主角朝向一定要在-z方向。可以外包父节点模型转到-z方向。roads:[路径1,路径2,路径3,...路径数据[p1,p2,p3{x,y,z}...pend]]
this.vx = this.speed * dir.x / len;
this.vy = this.speed * dir.y / len;
this.vz = this.speed * dir.z / len;
walk_to_next
–》lookAt—>朝向前方路径点有噪点,路径点本身不是平滑,导致摄像机在一个很短是时间内来回晃动
F:\cocos3D\009_test_physics\assets\scenes
#####【1】编辑物理场景,刚体,碰撞器,物理材质
BoxCollider矩形
:右键–》component
–》BoxCollider
SphereColliderComponent球形
:右键–》component
–》SphereCollider
刚体
-【计算物理运动,力,速度】
RigidBodyComponent刚体
:右键–》component
–》RigidBody
Mass
刚体质量LinearDamping
线性运动的阻尼AngularDamping
角速度阻尼IsKinematic
不会受物体运动,但可以改变物体的位置UseGravity
是否使用重力FixedRotation
刚体是否固定旋转LinearFactor
线性速度因子AngularFacto
旋转速度因子BoxColliderComponent
Material
物理材质—》弹力、摩擦系数
physic-material
Restitution
弹力Friction
摩擦系数IsTrigger
是否为触发器。只穿过物体,不改变物体运动。Center
中心Size
大小applyForce
加力import { _decorator, Component, Node, RigidBodyComponent } from "cc";
const { ccclass, property } = _decorator;
@ccclass("physics")
export class physics extends Component {
start () {
this.body = this.node.getComponent(RigidBodyComponent);
// this.body.applyForce(cc.v3(2000,3,500));//加力
// this.body.setLinearVelocity(cc.v3(5,-3,4));//线性速度
this.body.setAngularVelocity(cc.v3(0,45,0));// 旋转速度
}
}
按位与
按位或
左移:1<<1;0000 0001 <<1 = 0000 0010 左移了一位,右边0补齐。没左移一位*2
右移:0000 0010>> 0000 0001 把右边的去掉,高位,用数字最高位补齐 除以2
非常棒的技巧:可以用一个整数来表示集合(类型的集合)
8bit的整数位: 0000 0000
0000 0001 0000 0010 0000 0100 0000 1000
1<<0 1<<1 1<<2 1<<3 1<<4 1<<5 1<<6 1<<7
做成几个类的类型集合:[1,2,3] 0000 0001 | 0000 0010 | 0000 0100 = 0000 0111
判断,某种类型,是否在这个集合里面 value&(1<<类型)
BoxColliderComponent
,ICollisionEvent
,ITriggerEvent
当物理引擎检测到碰撞的时候,可以收到碰撞事件
onCollisionEnter
onCollisionStay
onCollisionExit
ICollisionEvent
type
:碰撞事件的类型selfCollider
:碰撞中的自己的碰撞器。otherCollider
:碰撞中的另一个碰撞器。contacts
:碰撞中的所有碰撞点的信息。setGroup/getCroup``setMask/getMask
Group, Mask:
(GroupA & MaskB) && (GroupB & MaskA)
setGroup/getCroup
setMask/getMask
console.log(PhysicsSystem.instance)
重力
:物理世界的重力值,默认为 (0, -10, 0)enable
:是否开启物理系统,默认为 trueallowSleep
:是否允许物理系统自动休眠,默认为 truemaxSubStep
:物理每帧模拟的最大子步数,默认为 2deltaTime
:物理每步模拟消耗的时间,注意不是每帧,默认为 1 / 60gravity
: 物理世界的重力值,默认为 (0, -10, 0)import {
_decorator,
Component,
Node,
RigidBodyComponent,
BoxColliderComponent,
ICollisionEvent,
ITriggerEvent,
PhysicsSystem,
} from "cc";
const {ccclass, property} = _decorator;
@ccclass("physics")
export class physics extends Component {
private body: RigidBodyComponent = null;
// private body: BoxColliderComponent = null;
onLoad() {
// this.body = this.node.getComponent(RigidBodyComponent);
// this.body.applyForce(cc.v3(2000,3,500));
}
start() {
this.body = this.node.getComponent(RigidBodyComponent);
// this.body.applyForce(cc.v3(2000,3,500));//加力
// this.body.setLinearVelocity(cc.v3(5,-3,4));//线性速度
// this.body.setAngularVelocity(cc.v3(0,45,0));// 旋转速度
// 碰撞
var collider = this.node.getComponent(BoxColliderComponent);
collider.on("onCollisionEnter", this.onCollisionEnter, this);
collider.on("onCollisionStay", this.onCollisionStay, this);
collider.on("onCollisionExit", this.onCollisionExit, this);
collider.on("onTriggerEnter", this.onTriggerEvent, this);
collider.on("onTriggerStay", this.onTriggerEvent, this);
collider.on("onTriggerExit", this.onTriggerEvent, this);
console.log(collider.getGroup(), collider.getMask())// 1 -1
collider.setMask((1 << 1) | (1 << 2)); // 设置碰撞关系
console.log(collider.getGroup(), collider.getMask())// 1 -1
// 0000 0010 0000 0100 --> 0000 0110
// cube:[1,mask(0000 0110)],
// group(0000 0001,mask -1)
console.log(PhysicsSystem.instance)
}
private onCollisionEnter(e: ICollisionEvent): void {
console.log('onCollisionEnter');
console.log(e)
}
private onCollisionStay(e: ICollisionEvent): void {
// console.log('onCollisionStay')
}
private onCollisionExit(e: ICollisionEvent): void {
console.log('onCollisionExit')
}
private onTriggerEvent(e: ITriggerEvent): void {
console.log('onTriggerEnter')
console.log(e);
console.log(e.type);
}
update(dt: number) {
}
}
type:sprite-frame
DEVICEMOTION
重力感应cc.systemEvent
EventKeyboard
: —>keyCode
, 按键码Touch, EventTouch
: 支持多点getLocation
;import { _decorator, Component, Node, systemEvent, SystemEvent, Vec2, SystemEventType,EventTouch,Touch ,EventKeyboard} from "cc";
const { ccclass, property } = _decorator;
@ccclass("gameMgr")
export class gameMgr extends Component {
start () {
//鼠标事件
systemEvent.on(SystemEventType.TOUCH_START,this.onTouchStart,this);
//重力感应
// systemEvent.on(SystemEventType.DEVICEMOTION,this.DEVICEMOTION,this);
// 键盘事件
systemEvent.on(SystemEventType.KEY_DOWN,this.keyDown,this)
}
onTouchStart(touch: Touch,event: EventTouch){
console.log(touch);
console.log(event); // 包含touch
var pos:Vec2 = event.touch.getLocation();
console.log(pos);
}
keyDown(e:EventKeyboard){
switch (e.keyCode) {
case 32: console.log(123);// 空格
break;
}
console.log(e)
}
destory(){
super.destroy();// 调用component destory
console.log('注销事件');
systemEvent.off(SystemEventType.TOUCH_START,this.onTouchStart,this);
systemEvent.off(SystemEventType.KEY_DOWN,this.keyDown,this);
}
}
var w_pos: Vec3 = this.camera.screenToWorld(cc.v3(pos.x, pos.y, 0))
2D坐标–》判断是3D物体
射线检测—》从某个点–》射线—》射线,这条射线碰撞到哪些物体
3D拾取:屏幕点击—》世界坐标,摄像机的世界坐标原点,(点击点-摄像机点),发射一条射线
自己创建射线对象:枪的坐标—》枪.forward—>构建一个自己的射线对象
射线对象:原点(Vec3),方向(Vec3)
被点击的物体一定要加物理碰撞器(trigger)
step1: 获取射线对象
step2: 射线碰撞到了哪些物体(物理模块,射线检测基于物理碰撞器),有没点击到cube
step3:使用物理引擎的接口来做射线检测
raycast
检测所有的碰撞盒,并记录所有被检测到的结果,通过 PhysicsSystem.instance.raycastResults 访问结果
worldRay
世界空间下的一条射线mask
掩码,默认为 0xffffffffmaxDistance
最大检测距离,默认为 10000000,目前请勿传入 Infinity 或 Number.MAX_VALUEqueryTrigger
是否检测触发器boolean
表示是否有检测到碰撞盒raycastClosest
检测所有的碰撞盒,并记录与射线距离最短的检测结果,通过 PhysicsSystem.instance.raycastClosestResult 访问结果geometry
几何体信息
1: systemEvent 全局事件单例;
2: 监听触摸: TOUCH_START/TOUCH_MOVE/TOUCH_END/TOUCH_CANCEL
鼠标事件: MOUSE_DOWN/MOUSE_MOVE/MOUSE_UP/
键盘: KEY_DOWN/KEY_UP
重力感应: DEVICEMOTION
3: UI 节点来监听事件。
1: 摄象机,触摸坐标转射线;
2: 射线检测与3D拾取;
ParticleSystemComponent
公共组件已保存
构建发布--设备方向
ctrl + shift + F
import { UIJoyStick } from "./UIJoyStick";
@property(UIJoyStick)
private stick: UIJoyStick = null; // 引入js文件
import {
_decorator,
Component,
Node,
RigidBodyComponent, // 刚体 物理组件
BoxColliderComponent,// 碰撞组件
ICollisionEvent,
ITriggerEvent,
PhysicsSystem, // 物理系统
systemEvent, // 系统
SystemEvent,
Vec2, // 二维向量
Vec3, // 三维向量
CanvasComponent, // canvas组件用到
renderer //
} from "cc";
_decorator,
Component,
Node,
F:\cocos3D\000_component\摇杆公共组件
无光照的 unlit、基于物理光照的 standard、skybox、粒子、sprite 等
public walk_on_road():void {}
private walk_to_next():void {}
destory(){
super.destroy();// 调用component destory
console.log('注销事件');
systemEvent.off(SystemEventType.TOUCH_START,this.onTouchStart,this);
}
UIJoyStick
摇杆、操纵杆ShadowCastingMode
需要显示阴影的模型组件设置为 ON///
3D游戏导入的资源,要把图片格式改为sprite-frame格式。否则不显示
tweenUtil(this.NodePos_arrow)
.stop()
.to(5, { z: targetZ, x: targetX, y: targetY })
.to(1, {})
.call(() => {
this.gameOver();
})
.start()
import { Quat, math } from 'cc';
const q_tmp = new Quat();
const out_Q = math.Quat.rotateAround(q_tmp, this.node._quat, cc.v3(), Math.PI * 0.1);
this.node.setRotation(out_Q.x, out_Q.y, out_Q.z, out_Q.w);
cc.v3(-1, 1, 0).normalizeSelf()
就不变形了var out_Q = cc.vmath.quat.rotateAround(q_tmp, this.node._quat, cc.v3(-1, 1, 0), Math.PI * 0.1);
this.node.setRotation(out_Q.x, out_Q.y, out_Q.z, out_Q.w);
let q_tmp = new Quat();
let v_tmp = new Vec3(0, 0, 1);
v_tmp.normalize();
let out_Q = Quat.rotateAround(q_tmp, this.zhizhen.rotation, cc.v3(0,0,1), Math.PI * 0.1);
this.zhizhen.setRotation(out_Q.x, out_Q.y, out_Q.z, out_Q.w);
var out_Q = math.Quat.rotateAround(q_tmp, this.xuanzhaunti.rotation, cc.v3(1, 0, 0), Math.PI * 0.1); //Math.PI * 0.1 * 180 / Math.PI = 18度
cc.v3(1, 0, 0)
是绕X轴旋转,cc.v3(0, 1, 0)
是绕Y轴旋转,cc.v3(0, 0, 1)
是绕Z轴旋转, onLoad(){
this.lab.on(Node.EventType.TOUCH_MOVE, this.callback, this);
}
callback(EventTouch){
let dif = EventTouch.getDelta();
let q_tmp = new Quat();
let v_tmp = new Vec3(-dif.y, dif.x, 0);
v_tmp.normalize();
let out_Q = Quat.rotateAround(q_tmp, this.testNode.rotation, v_tmp, Math.PI * 0.01);
this.testNode.setRotation(out_Q.x, out_Q.y, out_Q.z, out_Q.w);
}
/**
* @zh 根据欧拉角信息计算四元数,旋转顺序为 YZX
*/
static fromEuler<Out extends __internal.$cocos.$core.$math.$type_define.IQuatLike>(out: Out, x: number, y: number, z: number): Out;
/**
* @zh 根据四元数计算欧拉角,返回角度 x, y 在 [-180, 180] 区间内, z 默认在 [-90, 90] 区间内,旋转顺序为 YZX
* @param outerZ z 取值范围区间改为 [-180, -90] U [90, 180]
*/
static toEuler<Out extends __internal.$cocos.$core.$math.$type_define.IVec3Like>(out: Out, q: __internal.$cocos.$core.$math.$type_define.IQuatLike, outerZ?: boolean): Out;
/**
* @zh 将当前四元数转化为欧拉角(x-y-z)并赋值给出口向量。
* @param out 出口向量。
*/
getEulerAngles(out: Vec3): Vec3;
/**
* @zh 根据欧拉角信息计算四元数,旋转顺序为 YZX
*/
static fromEuler<Out extends __internal.$cocos.$core.$math.$type_define.IQuatLike>(out: Out, x: number, y: number, z: number): Out;
/**
* @zh 根据四元数计算欧拉角,返回角度 x, y 在 [-180, 180] 区间内, z 默认在 [-90, 90] 区间内,旋转顺序为 YZX
* @param outerZ z 取值范围区间改为 [-180, -90] U [90, 180]
*/
static toEuler<Out extends __internal.$cocos.$core.$math.$type_define.IVec3Like>(out: Out, q: __internal.$cocos.$core.$math.$type_define.IQuatLike, outerZ?: boolean): Out;
/**
* @zh
* 通过欧拉角设置世界旋转
* @param x - 目标欧拉角的 X 分量
* @param y - 目标欧拉角的 Y 分量
* @param z - 目标欧拉角的 Z 分量
*/
setWorldRotationFromEuler(x: number, y: number, z: number): void;
/**
* @zh
* 通过欧拉角设置本地旋转
* @param x - 目标欧拉角的 X 分量
* @param y - 目标欧拉角的 Y 分量
* @param z - 目标欧拉角的 Z 分量
*/
setRotationFromEuler(x: number, y: number, z: number): void;
/**
* @zh
* 设置本地旋转
* @param x 目标本地旋转的 X 分量
* @param y 目标本地旋转的 Y 分量
* @param z 目标本地旋转的 Z 分量
* @param w 目标本地旋转的 W 分量
*/
setRotation(x: number, y: number, z: number, w: number): void;
/**
* @zh
* 设置本地旋转
* @param rotation 目标本地旋转
*/
setRotation(rotation: Quat): void;
/**
* @zh 将当前四元数转化为欧拉角(x-y-z)并赋值给出口向量。
* @param out 出口向量。
*/
getEulerAngles(out: Vec3): Vec3;