CocosCreator 2.x 二维手牌 和选牌逻辑
时间: 2025-07-20 15:30:09 浏览: 15
<think>我们正在使用Cocos Creator 2.x版本实现一个二维手牌展示和选牌逻辑。根据游戏需求,我们通常需要将手牌以扇形展开,并支持点击选择牌。下面将分步骤介绍实现方法。
### 1. 手牌布局(扇形排列)
在卡牌游戏中,手牌通常以扇形展开,我们可以通过计算每张牌的位置和旋转角度来实现。
#### 实现思路:
- 确定扇形中心点(一般位于屏幕底部中央)。
- 根据手牌数量计算每张牌之间的角度间隔。
- 根据角度和半径计算每张牌的位置,并设置旋转角度。
#### 代码示例(在TypeScript脚本中):
```typescript
const {ccclass, property} = cc._decorator;
@ccclass
export default class HandCardLayout extends cc.Component {
@property(cc.Node)
centerNode: cc.Node = null; // 扇形中心点,通常位于屏幕底部中央
@property
radius: number = 200; // 扇形半径
@property
maxAngle: number = 60; // 最大扇形角度(整个扇形展开的角度)
start() {
this.layoutCards();
}
layoutCards() {
// 获取所有手牌节点(假设都是当前节点的子节点)
let cards = this.node.children;
let count = cards.length;
// 如果没有牌,直接返回
if (count === 0) return;
// 计算每张牌的角度间隔
let angleStep = count > 1 ? this.maxAngle / (count - 1) : 0;
// 起始角度(从扇形左侧开始)
let startAngle = -this.maxAngle / 2;
for (let i = 0; i < count; i++) {
let card = cards[i];
// 当前角度
let angle = startAngle + i * angleStep;
// 将角度转换为弧度
let radian = angle * Math.PI / 180;
// 计算位置
let x = this.radius * Math.sin(radian);
let y = this.radius * Math.cos(radian) - this.radius; // 减去半径使底部对齐
// 设置位置
card.setPosition(cc.v2(x, y));
// 设置旋转(围绕中心点旋转,所以旋转角度为-angle)
card.angle = -angle;
}
}
}
```
### 2. 选牌交互
当玩家点击一张牌时,我们希望这张牌能够被选中(例如向上移动突出显示,或者放大),同时其他牌恢复原位。
#### 实现思路:
- 为每张牌绑定触摸事件。
- 当触摸开始时,将被点击的牌置顶(避免被其他牌遮挡),并向上移动(或放大)以突出显示。
- 当触摸结束时,判断是否选中,执行选牌逻辑(如出牌)。
#### 代码示例(在每张牌的组件上):
```typescript
const {ccclass, property} = cc._decorator;
@ccclass
export default class Card extends cc.Component {
// 记录原始位置和旋转,用于取消选中时恢复
private originalPos: cc.Vec2 = cc.v2(0, 0);
private originalAngle: number = 0;
private originalZIndex: number = 0;
// 是否被选中
private isSelected: boolean = false;
onLoad() {
// 保存原始状态
this.originalPos = this.node.position;
this.originalAngle = this.node.angle;
this.originalZIndex = this.node.zIndex;
// 注册触摸事件
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
}
onTouchStart() {
// 触摸开始时,将牌置顶并移动到突出位置
this.node.zIndex = 100; // 置顶
// 向上移动一定距离
let moveUp = cc.moveBy(0.1, cc.v2(0, 50)).easing(cc.easeCubicActionOut());
// 可以同时放大
let scaleUp = cc.scaleTo(0.1, 1.2);
this.node.runAction(cc.spawn(moveUp, scaleUp));
this.isSelected = true;
}
onTouchEnd() {
if (this.isSelected) {
// 执行选牌逻辑,例如出牌
this.playCard();
}
// 恢复状态
this.resetCard();
}
onTouchCancel() {
this.resetCard();
}
// 重置牌的状态(位置、旋转、缩放)
resetCard() {
this.isSelected = false;
this.node.zIndex = this.originalZIndex;
// 恢复位置和旋转
let moveBack = cc.moveTo(0.1, this.originalPos);
let scaleBack = cc.scaleTo(0.1, 1);
this.node.runAction(cc.spawn(moveBack, scaleBack));
}
// 执行出牌逻辑
playCard() {
// 这里可以发送事件通知游戏逻辑出牌
// 例如:cc.systemEvent.emit('card-played', this.node);
// 然后从手牌中移除
this.node.removeFromParent();
}
}
```
### 3. 注意事项
- **动态添加/移除手牌**:当手牌数量变化时,需要重新布局。可以在`HandCardLayout`组件中提供一个公共方法供外部调用。
- **性能优化**:如果手牌数量很多,可以考虑使用对象池管理牌节点。
- **事件通信**:选牌后通常需要通知游戏主逻辑,可以使用事件系统(`cc.systemEvent`)进行通信。
### 总结
以上实现了二维手牌的扇形布局和选牌交互。通过布局组件控制位置和旋转,通过每张牌上的组件处理触摸事件。可以根据实际需求调整参数和动画效果。
§§相关问题§§
1. Cocos Creator 2.x中如何优化大量卡牌的渲染性能?
2. 如何实现卡牌的拖拽出牌?
3. 在Cocos Creator中如何实现卡牌之间的遮挡关系(例如被选中的牌要显示在最前面)?</think>在 Cocos Creator 2.x 中实现二维手牌展示和选牌逻辑,主要分为布局和交互两个核心部分。以下是具体实现方案:
### 一、手牌布局实现(扇形排列)
```typescript
// CardLayout.ts
const {ccclass, property} = cc._decorator;
@ccclass
export default class CardLayout extends cc.Component {
@property(cc.Prefab)
cardPrefab: cc.Prefab = null; // 卡牌预制体
@property
radius: number = 200; // 扇形半径
@property
maxAngle: number = 60; // 最大展开角度
@property
selectOffset: number = 30; // 选中时上浮距离
private cards: cc.Node[] = []; // 存储卡牌节点
// 初始化手牌
initCards(cardCount: number) {
this.node.removeAllChildren();
this.cards = [];
for (let i = 0; i < cardCount; i++) {
const card = cc.instantiate(this.cardPrefab);
card.getComponent('Card').init(i); // 初始化卡牌数据
this.node.addChild(card);
this.cards.push(card);
}
this.updateLayout();
}
// 更新扇形布局
updateLayout() {
const count = this.cards.length;
const angleStep = count > 1 ? this.maxAngle / (count - 1) : 0;
const startAngle = -this.maxAngle / 2;
this.cards.forEach((card, index) => {
const angle = startAngle + index * angleStep;
const radian = angle * Math.PI / 180;
// 计算位置
const x = this.radius * Math.sin(radian);
const y = this.radius * Math.cos(radian) - this.radius;
// 设置位置和旋转
card.setPosition(cc.v2(x, y));
card.angle = -angle;
// 设置层级(防止重叠遮挡)
card.zIndex = index;
});
}
}
```
### 二、选牌交互逻辑
```typescript
// Card.ts
const {ccclass, property} = cc._decorator;
@ccclass
export default class Card extends cc.Component {
private cardId: number = 0;
private originalPos: cc.Vec2 = cc.Vec2.ZERO;
private isSelected: boolean = false;
// 初始化卡牌数据
init(id: number) {
this.cardId = id;
this.originalPos = this.node.position.clone();
// 添加触摸事件
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
}
onTouchStart() {
this.isSelected = !this.isSelected;
if (this.isSelected) {
// 选中效果:上浮+放大
cc.tween(this.node)
.to(0.2, {
position: cc.v3(this.originalPos.x, this.originalPos.y + 30),
scale: 1.2
})
.start();
this.node.zIndex = 100; // 置顶显示
} else {
// 取消选中:恢复原位
cc.tween(this.node)
.to(0.2, {
position: this.originalPos,
scale: 1
})
.start();
this.node.zIndex = this.cardId; // 恢复层级
}
// 发送选牌事件(参数:卡牌ID,选中状态)
cc.systemEvent.emit('card-selected', this.cardId, this.isSelected);
}
// 重置状态(出牌后调用)
reset() {
this.isSelected = false;
this.node.position = this.originalPos;
this.node.scale = 1;
this.node.zIndex = this.cardId;
}
}
```
### 三、核心功能说明
1. **动态扇形布局**
- 根据手牌数量自动计算角度间隔
- 通过三角函数计算每张牌的位置 $$x = r \cdot \sin(\theta), y = r \cdot \cos(\theta) - r$$
- 卡牌旋转角度与位置角度相反
2. **选牌交互特性**
- 点击切换选中状态
- 选中时上浮+放大视觉反馈
- 动态调整渲染层级(zIndex)
- 使用cc.tween实现平滑动画
3. **事件通信**
- 通过 `cc.systemEvent` 发送选牌事件
- 主游戏逻辑监听事件处理选牌结果
### 四、优化建议
1. **性能优化**:
```typescript
// 在CardLayout中添加
lateUpdate() {
if (this.needUpdateLayout) {
this.updateLayout();
this.needUpdateLayout = false;
}
}
```
2. **多选支持**:
```typescript
// 在选牌事件中处理多选逻辑
cc.systemEvent.on('card-selected', (id, selected) => {
if (selected) {
this.selectedCards.push(id);
} else {
const index = this.selectedCards.indexOf(id);
if (index > -1) this.selectedCards.splice(index, 1);
}
});
```
3. **拖拽出牌**:
```typescript
// 在Card.ts中添加
onTouchMove(event: cc.Event.EventTouch) {
if (this.isSelected) {
const delta = event.getDelta();
this.node.x += delta.x;
this.node.y += delta.y;
}
}
```
### 五、实现效果
1. 手牌以扇形展开,牌间距均匀
2. 点击单张牌时上浮突出显示
3. 支持多张牌同时选中
4. 选中的牌自动置顶显示
5. 出牌后自动更新布局
> **注意**:实际项目中需根据游戏规则调整选牌逻辑(如最多可选数量、出牌条件判断等)。可通过 `cc.view.getVisibleSize()` 获取屏幕尺寸动态调整扇形半径[^1]。
阅读全文
相关推荐



















