В конце 2017 года WeChat выпустил новую версию, ориентированную на концепцию «мини-игр». Игра H5 снова стала популярной. Игра, открытая в новой версии WeChat, представляет собой «прыгающую» игру с высокой степенью играбельности. а так же в интернете появились разного рода игры.Языковая версия плагина,я видел плагин построенный на nodejs несколько дней назад,нужно вручную нажимать на скриншот,чтобы определить текущую и следующую позицию и затем прыгать, поэтому я начал идею использовать Canvas для реализации изображения, а реализация есть позже.Если он автоматически прыгает, это все живо. Сегодня поговорим о распознавании изображений полностью.
Давайте посмотрим на окончательный эффект:
Весь код размещен: https://github.com/ksky521/wechat-jump-game-hack Добро пожаловать, попробуйте сами
Принцип обработки изображений Canvas
Доступ к холсту можно получить черезdrawImage
Добавьте изображение выше, затем передайтеgetImageData
способ получитьimageData
объект, этот объект включаетdata
,width
а также height
, где data — ширина изображенияheightМассив 4 длины, каждый пиксель представлен в массиве как: RGBA
Четыре значения от 0 до 255, а именно значения Red, Green, Blue и Alpha.
этимimageData.data
Для операции обхода вы можете использовать сравнение различий изображения, чтобы найти край объекта на изображении и центральную точку объекта, или вы можете сопоставить объект в определенном цветовом диапазоне на изображении, чтобы найти положение "маленький человек".
Функция сравнения разницы значений цвета
Сначала введите функциюtolerenceHelper
, который используется для сравнения цветовой разницы, то есть передачи значения, которое необходимо сравнитьr
,g
а также b
, а затем сравнить сrt
,gt
,bt
и диапазон разницыt
функция для сравнения, в диапазоне возвращаетtrue
.
-
function tolerenceHelper(r, g, b, rt, gt, bt, t) {
-
return r > rt - t && r < rt + t
-
&& g > gt - t && g < gt + t
-
&& b > bt - t && b < bt + t;
-
}
скопировать код
Получить текущее местоположение злодея
Злодей получает позицию, сравнивая разницу.Во-первых, через цветовой диапазон фиолетового злодея на скриншоте можно примерно получить значение цвета злодея:
// 小人的颜色值
const playerR = 40;
const playerG = 43;
const playerB = 86;
скопировать код
Это значение можно получить, взяв цвет из нижнего центра злодея. Таким образом, способ найти нижнюю центральную точку злодея находится в пределах определенного диапазона (т.е.tolerenceHelper
t, значение здесь равно 16) поиск, если точка пикселя rgb находится в этом диапазоне, она будет добавлена для выбора, а положение самой нижней точки (максимум y) в конечном наборе точек пикселя равно y, x точки, где находится нижний центр злодея, является центральным положением максимальной и минимальной ширины. Для лучшего понимания нарисовал картинку:
Три картинки нижеt=16
,t
=26
,t
=36
Для эффекта раздельной идентификации, для простоты идентификации я установил цвет совпадающих пикселей на красный (rgb=255, 0, 0).
Чтобы быть точным, чтобы предотвратить интерференцию похожих цветовt=16
Достаточно, так что нижняя позиция злодеяpos
Просто получил:
-
// x, y
-
pos[0] = Math.floor((maxX + minX) / 2);
-
pos[1] = maxY;
скопировать код
оптимизация
С первого взгляда легко увидеть, что злодей не может быть вверху и внизу изображения, а находится в центральной области изображения, поэтому его можно рассматривать непосредственно с высоты изображения.height/4
~ height
*3
/4
, что может увеличить ненужную рабочую нагрузку.
Полный код для поиска очков злодея выглядит следующим образом.
function getCurCenter(data, width, height) {
// 小人的颜色值
const playerR = 40;
const playerG = 43;
const playerB = 86;
let minX = Infinity;
let maxX = -1;
let maxY = -1;
// 找到小人当前的底部位置
let pos = [0, 0];
let startY = Math.floor(height / 4);
let endY = Math.floor(height * 3 / 4);
for (let x = 0; x < width; x++) {
for (let y = startY; y < endY; y++) {
let i = y * (width * 4) + x * 4;
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
if (y > pos[1] && tolerenceHelper(r, g, b, playerR, playerG, playerB, 16)) {
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
}
}
pos[0] = Math.floor((maxX + minX) / 2);
pos[1] = maxY;
// console.log(`player position (x, y)= (${pos[0]}, ${pos[1]})`);
return pos;
}
скопировать код
Получить позицию для прыжка
Как получить позицию, куда злодей прыгает дальше?
По вышеописанной логике мы все же начинаем с высоты картинкиheight/4
~ height
*3
/4
Поиск по диапазону, это мы сначала берем текущий цвет фона, а потом сканируем изображение в пределах диапазона высот.Когда появляется первая точка сильно отличающаяся от цвета фона, это и есть основное значение цвета следующего объекта! Если это четырехугольник или что-то подобное, то эта точка является вершиной!
Зная значение основного цвета объекта, мы можем продолжить сканирование исходя из этого значения.Пиксели в этом диапазоне значений цвета являются верхней поверхностью объекта, а затем по координатам пикселей на верхней поверхностиminY
а также maxY
Получите координаты центральной точки (круги и квадраты симметричны, поэтому можно использовать этот метод).
Посмотрите на картинку, чтобы понять:
На следующем рисунке цвет фона окрашен в красный цвет, чтобы вы могли видеть, что первая идентифицированная точка является вершиной (такой же, как круг)
оптимизация
-
После нахождения вершины следующая строка точно не maxY.По аналогии можно смело увеличить значение Y на 60 пикселей, то есть начать искать центральную точку на 60 пикселей вниз от вершины;
-
Кроме того, вы можете сузить диапазон поиска Y до значения Y центральной точки злодея, найденного на предыдущем шаге, то есть значение y равно
height/4~Math.min(height*3/4, 小人中心Y)
, так что даже если мы не найдем maxY, мы можем взять центральную точку со злодеем за нижнюю, чтобы гарантировать, что следующая позиция прыжка не будет максимально превышать диапазон верхней поверхности объекта. -
Ищем maxY, поэтому пока есть точка того же цвета, что и пиксель вершины (в пределах диапазона), эту линию (координата Y та же, X разная) искать не надо, т.к. поиск бессмыслен, а значение Y остается неизменным. , так что вы можете напрямую
break
Выйдите из цикла и найдите следующий Y
полный код
function getNextCenter(data, width, height, y = -1) {
let startY = Math.floor(height / 4);
let endY = Math.floor(height * 3 / 4);
// 去除背景色
let startX = startY * width * 4;
let r = data[startX],
g = data[startX + 1],
b = data[startX + 2];
let maxY = -1;
let apex = [];
let pos = [0, 0];
// 保证从当前小人位置底部点往上
endY = Math.min(endY, y);
let endX = width;
let gapCount = 0;
for (let y = startY; y < endY; y++) {
let find = 0;
for (let x = 1; x < endX; x++) {
let i = y * (width * 4) + x * 4;
let rt = data[i];
let gt = data[i + 1];
let bt = data[i + 2];
// 不是默认背景颜色
if (!tolerenceHelper(rt, gt, bt, r, g, b, 30)) {
if (apex.length === 0) {
if (!tolerenceHelper(data[i + 4], data[i + 5], data[i + 6], r, g, b, 30)) {
//椭圆形找中心,往后找30个像素点
let len = 2;
while (len++ !== 30) {
i += len * 4;
if (tolerenceHelper(data[i + 4], data[i + 5], data[i + 6], r, g, b, 30)) {
break;
}
}
x += len;
}
//找出顶点
apex = [rt, gt, bt, x, y];
pos[0] = x;
// 减少循环范围
endX = x;
break;
} else if (tolerenceHelper(rt, gt, bt, apex[0], apex[1], apex[2], 5)) {
//存在顶点了,则根据颜色值开始匹配
maxY = Math.max(maxY, y);
find = x;
break;
}
}
}
if (apex.length !== 0 && !find) {
gapCount++;
}
if (gapCount === 3) {
break;
}
}
pos[1] = Math.floor((maxY + apex[4]) / 2);
// console.log(points_top, points_left, points_right);
console.log(`next position center (x,y)=${pos[0]},${pos[1]}`);
return pos;
}
скопировать код
наконец
В течение всего процесса тестирования я также пробовал другие методы, такие как сначала найти край, а затем найти центральную точку.
Для удобства отладки я отделяю эту часть кода как маршрутизатор, вы можете напрямую GitHub клонировать код для доступаlocalhost:3000/test
, а затем попробуйте изменить его и посмотрите на эффект.
В этой статье рассказывается, как определить "человечка" на картинке и место следующего прыжка. В следующей статье будет рассказано, как заставить "человечка" перепрыгивать автоматически, так что следите за обновлениями.
EOF @Sanshuiqing, пожалуйста, не перепечатывайте без разрешения. Почувствуйте себя полезным, добро пожаловать, чтобы обратить внимание на мой общедоступный номер