add offset to multi avatar layer

This commit is contained in:
bridge
2025-11-24 23:20:39 +08:00
parent 9295d71a90
commit 991f9908d6
3 changed files with 81 additions and 7 deletions

View File

@@ -8,6 +8,7 @@ import { useSharedTicker } from './composables/useSharedTicker'
const props = defineProps<{
avatar: AvatarSummary
tileSize: number
offset?: { x: number; y: number }
}>()
const emit = defineEmits<{
@@ -21,8 +22,12 @@ const targetX = ref(props.avatar.x)
const targetY = ref(props.avatar.y)
// Current render position (pixel coordinates)
const currentX = ref(props.avatar.x * props.tileSize + props.tileSize / 2)
const currentY = ref(props.avatar.y * props.tileSize + props.tileSize / 2)
// Initial position includes offset immediately to avoid "jumping" on spawn if possible,
// but props.offset might be undefined initially.
const initialOffsetX = props.offset?.x ?? 0
const initialOffsetY = props.offset?.y ?? 0
const currentX = ref((props.avatar.x + initialOffsetX) * props.tileSize + props.tileSize / 2)
const currentY = ref((props.avatar.y + initialOffsetY) * props.tileSize + props.tileSize / 2)
// Watch for prop updates (server ticks)
watch(() => [props.avatar.x, props.avatar.y], ([newX, newY]) => {
@@ -31,8 +36,11 @@ watch(() => [props.avatar.x, props.avatar.y], ([newX, newY]) => {
})
useSharedTicker((delta) => {
const destX = targetX.value * props.tileSize + props.tileSize / 2
const destY = targetY.value * props.tileSize + props.tileSize / 2
const offsetX = props.offset?.x ?? 0
const offsetY = props.offset?.y ?? 0
const destX = (targetX.value + offsetX) * props.tileSize + props.tileSize / 2
const destY = (targetY.value + offsetY) * props.tileSize + props.tileSize / 2
const speed = 0.1 * delta
@@ -90,8 +98,8 @@ const nameStyle = {
fontFamily: '"Microsoft YaHei", sans-serif',
fontSize: 42, // Increased from 36
fontWeight: 'bold',
fill: 0xffffff,
stroke: { color: 0x000000, width: 6 }, // Thicker stroke
fill: '#ffffff',
stroke: { color: '#000000', width: 6 }, // Thicker stroke
align: 'center',
dropShadow: {
color: '#000000',
@@ -100,7 +108,7 @@ const nameStyle = {
distance: 2,
alpha: 0.8
}
}
} as any
function handlePointerTap() {
emit('select', {

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { useWorldStore } from '../../stores/world'
import AnimatedAvatar from './AnimatedAvatar.vue'
import { computed } from 'vue'
import { calculateVisualOffsets } from './utils/avatarLayout'
const worldStore = useWorldStore()
const TILE_SIZE = 64
@@ -9,6 +11,10 @@ const emit = defineEmits<{
(e: 'avatarSelected', payload: { type: 'avatar'; id: string; name?: string }): void
}>()
const avatarOffsets = computed(() => {
return calculateVisualOffsets(worldStore.avatarList)
})
function handleAvatarSelect(payload: { type: 'avatar'; id: string; name?: string }) {
emit('avatarSelected', payload)
}
@@ -21,6 +27,7 @@ function handleAvatarSelect(payload: { type: 'avatar'; id: string; name?: string
:key="avatar.id"
:avatar="avatar"
:tile-size="TILE_SIZE"
:offset="avatarOffsets.get(avatar.id)"
@select="handleAvatarSelect"
/>
</container>

View File

@@ -0,0 +1,59 @@
export interface Point {
x: number
y: number
}
interface PositionedObject {
id: string
x: number
y: number
}
/**
* 计算角色在格子内的视觉偏移量,避免重叠
* @param items 包含坐标的对象列表
* @param radius 偏移半径相对于格子大小的比例0.25 表示 1/4 格子宽)
*/
export function calculateVisualOffsets<T extends PositionedObject>(
items: T[],
radius: number = 0.25
): Map<string, Point> {
const groups = new Map<string, T[]>()
const offsets = new Map<string, Point>()
// 1. 按坐标分组
for (const item of items) {
const key = `${item.x},${item.y}`
if (!groups.has(key)) {
groups.set(key, [])
}
groups.get(key)!.push(item)
}
// 2. 计算每组的偏移
for (const group of groups.values()) {
const count = group.length
// 只有一个人时,不需要偏移,居中显示
if (count <= 1) {
offsets.set(group[0].id, { x: 0, y: 0 })
continue
}
// 多人时,按圆形分布
// 无论多少人,都均匀分布在圆周上
// 起始角度 -PI/2 (即正上方),让排列更符合直觉
const angleStep = (Math.PI * 2) / count
group.forEach((item, index) => {
const angle = index * angleStep - Math.PI / 2
offsets.set(item.id, {
x: Math.cos(angle) * radius,
y: Math.sin(angle) * radius
})
})
}
return offsets
}