小球瞄准线在2D小游戏开发中是常用的功能,找了好久发现网上都是CocosCreator2.x相关的教程,如白玉无冰老师的《反复横跳的瞄准线!…》、《creator 反复横跳瞄准线的实现》From飞猪001,刚好第一份实习时有个3.x项目需要小球瞄准功能,但是当时由于3.x更新了系列2.x的API一直没弄懂做不出来,今天解决了就做个记录(这里要特别感谢键兴哥哥的指导)。
基本准备: 创建2D项目==>新建场景==>设置项目的设计宽高:宽540高960
- 在Canvas画布下创建Sprite节点名称为touch_node和Graphics节点名为graphic_line
- 新建脚本Test.ts挂到Canvas节点上,复制以下下代码(保存Canvas节点上找到Test组件挂上touch_node和graphic_line节点):
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167import { _decorator, BoxCollider2D, Component, EPhysics2DDrawFlags, ERaycast2DType, ERigidBody2DType, Node, PhysicsSystem2D, RigidBody2D, UITransform, Vec2, Vec3, RigidBody, Graphics } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Test')
export class Test extends Component {
line_height = 2000
@property(Graphics)
graphic_line: Graphics = null
@property(Node)
private touch_node: Node = null;
protected onLoad(): void {
PhysicsSystem2D.instance.enable = true;
PhysicsSystem2D.instance.debugDrawFlags = EPhysics2DDrawFlags.Aabb |
EPhysics2DDrawFlags.Pair |
EPhysics2DDrawFlags.CenterOfMass |
EPhysics2DDrawFlags.Joint |
EPhysics2DDrawFlags.Shape;
this.initModule()
}
protected start(): void {
this.touch_node.on(Node.EventType.TOUCH_START, (event) => {
console.log('true')
this.resetLine(event.getLocation())
}, this)
this.touch_node.on(Node.EventType.TOUCH_MOVE, (event) => {
this.resetLine(event.getLocation())
}, this)
}
update(deltaTime: number) {
}
/**
* 刷新线
* @param touch_point
*/
resetLine(touch_point: Vec2) {
this.graphic_line.clear();
let temp = this.graphic_line.node.getComponent(UITransform).convertToWorldSpaceAR(Vec3.ZERO);
let start_point = new Vec2(temp.x, temp.y)
let sub_vec = new Vec2(Vec2.ZERO)
Vec2.subtract(sub_vec, touch_point, start_point)
// let sub_vec = touch_point.sub(start_point);
let end_point = new Vec2(Vec2.ZERO)
let a = new Vec2(Vec2.ZERO);
Vec2.divide(a, sub_vec, new Vec2(Vec2.len(sub_vec), Vec2.len(sub_vec)))
let b = new Vec2()
Vec2.multiplyScalar(b, a, this.line_height)
end_point = b.add(start_point)
// let end_point = sub_vec.div(sub_vec.mag()).mul(this.line_height).add(start_point);
this.physicalRayCast(start_point, end_point, this.line_height);
this.graphic_line.stroke();
}
/**
* 射线检查
* @param p1
* @param p2
* @param line_mag
*/
physicalRayCast(p1: Vec2, p2: Vec2, line_mag: number) {
//射线检测
var results = PhysicsSystem2D.instance.raycast(p1, p2,
ERaycast2DType.Closest)[0];
let sub_vec: Vec2 = new Vec2();
if (results) {
Vec2.subtract(sub_vec, results.point, p1)
// sub_vec = results.point.sub(p1);
//计算长度
let draw_mag = Vec2.len(sub_vec);
//得到入射向量单位
Vec2.divide(sub_vec, sub_vec, new Vec2(draw_mag, draw_mag));
if (line_mag - draw_mag > 0) {
let temp = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(p1.x, p1.y, 0))
this.drawLine(new Vec2(temp.x, temp.y), sub_vec, draw_mag);
//法向量
p1 = results.point;
//反射法向量
let mul = new Vec2()
Vec2.multiply(mul, results.normal, new Vec2(2 * sub_vec.dot(results.normal), 2 * sub_vec.dot(results.normal)))
Vec2.subtract(p2, sub_vec, mul)
// p2 = sub_vec.sub(results.normal.mul(2*sub_vec.dot(results.normal)));
p2.multiply(new Vec2(line_mag - draw_mag, line_mag - draw_mag))
// p2.mul(line_mag - draw_mag, p2);
//反射点
p2.add(p1)
this.physicalRayCast(p1, p2, line_mag - draw_mag);
} else {
let temp = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(p1.x, p1.y, 0))
this.drawLine(new Vec2(temp.x, temp.y), sub_vec, line_mag);
}
} else {
Vec2.subtract(sub_vec, p2, p1)
// sub_vec = p2.sub(p1);
let draw_mag = Vec2.len(sub_vec);
Vec2.divide(sub_vec, sub_vec, new Vec2(draw_mag, draw_mag));
// sub_vec.divSelf(sub_vec.mag());
let temp = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(p1.x, p1.y, 0))
this.drawLine(new Vec2(temp.x, temp.y), sub_vec, line_mag);
}
}
/**
* 画线
* @param start_point
* @param sub_vec
* @param line_mag
*/
drawLine(start_point: Vec2, sub_vec: Vec2, line_mag: number) {
let a = new Vec2()
Vec2.multiplyScalar(a, sub_vec, 20)
let end_point = new Vec2()
Vec2.add(end_point, start_point, a)
this.graphic_line.moveTo(start_point.x, start_point.y);
this.graphic_line.lineTo(end_point.x, end_point.y);
line_mag -= 30;
if (line_mag > 0) {
let t = new Vec2()
Vec2.multiplyScalar(t, sub_vec, 10)
this.drawLine(end_point.add(t), sub_vec, line_mag);
}
}
initModule() {
let width = this.node.getComponent(UITransform).width
let height = this.node.getComponent(UITransform).height;
let node = new Node();
let body = node.addComponent(RigidBody2D);
body.type = ERigidBody2DType.Static;
this._addBound(node, 0, height / 2, width, 20);
this._addBound(node, 0, -height / 2, width, 20);
this._addBound(node, -width / 2, 0, 20, height);
this._addBound(node, width / 2, 0, 20, height);
node.parent = this.node;
}
_addBound(node, x, y, width, height) {
let collider = node.addComponent(BoxCollider2D); //PhysicsBoxCollider
collider.offset.x = x;
collider.offset.y = y;
collider.size.width = width;
collider.size.height = height;
}
}
这样一条基本的反复横跳瞄准线就完成了,当触摸touch_node时瞄准线就会随着手指的移动而发生变化。在自己的项目中也可以对这条线进行系列美化。具体后面会放在后面的升级代码中。