Quelles astuces Java peut-il implémenter pour obtenir un serpent gourmand? Découvrez la version d'icône de bureau

Prenez l'habitude d'écrire ensemble ! C'est le 7ème jour de ma participation au "Nuggets Daily New Plan · April Update Challenge", cliquez pour voir les détails de l'événement .

avant-propos

Si je vous demande, qu'est-ce qu'une icône du bureau peut faire d'autre que double-cliquer pour démarrer un programme ?

Bien sûr, lorsque le jeu est joué, vous verrez donc cette image ci-dessous.

875na-3i19d.gif

Adresse de téléchargement : houxinlin.com:8080/Game.rar

Notez que le serveur à cette adresse est implémenté en assembleur dans les chapitres précédents. Je veux voir s'il peut être facilement pris en charge dans la réalité. S'il n'est pas accessible, veuillez laisser un message.

Voici comment procéder.

Principe de mise en œuvre

Cet article doit connaître la plupart des choses, il suffit de regarder l'officiel. Si vous voulez en savoir plus, vous devez d'abord comprendre le développement de Windows. Cet article ne décrira pas chaque point de connaissance en détail.

Il y a deux problèmes que java ne peut pas implémenter, c'est-à-dire l'enregistrement des raccourcis clavier et le déplacement des icônes du bureau, donc cette partie est implémentée avec c, et voici quelques-unes des interfaces jni définies.

public class GameJNI {

    public native void registerHotkeyCallback(HotkeyCallback callback);

    public native  int getDesktopIcon();

    public native void moveDesktopIcon(int index,int x,int y);
}
复制代码

Si vous ne comprenez pas le mécanisme de Windows, vous ne pourrez peut-être pas comprendre comment déplacer les icônes du bureau. En bref, Windows est basé sur le mécanisme des messages. Le message indique à une fenêtre ce qui s'est passé. Par exemple, lorsque vous enregistrez un hotkey, le raccourci s'appelle Appuyez dessus, puis vous obtiendrez une réponse dans la fonction de fenêtre de Windows, la fonction de fenêtre obtiendra l'identifiant du message spécifique, l'identifiant de la touche de raccourci est WM_HOTKEY, et il y a d'autres paramètres passés par le système ensemble, comme wParam, lParam.

Le déplacement d'une icône, par contre, envoie un message à la fenêtre où se trouve l'icône, indiquant que quelqu'un veut repositionner une icône.

Regardez d'abord l'implémentation de registerHotkeyCallback.


void RegisterHotkey(int id,int vkCode) {
	if (RegisterHotKey(NULL, id, 0, vkCode) == 0) {
            printf("fail %d\n", vkCode);
	}
}
JNIEXPORT void JNICALL Java_com_h_game_jni_GameJNI_registerHotkeyCallback
(JNIEnv *env, jobject thisObj, jobject callbackObject) {

	RegisterHotkey(LEFT_ID,37);
	RegisterHotkey(UP_ID,38);
	RegisterHotkey(RIGHT_ID,39);
	RegisterHotkey(DOWN_ID,40);
	fflush(stdout);

	MSG lpMsg = {0};
	while (GetMessage(&lpMsg, NULL, 0, 0)!=0){
		jclass thisClass = (*env)->GetObjectClass(env, callbackObject);
		jmethodID hotKeyCallbackMethodId = (*env)->GetMethodID(env, thisClass, "hotkey", "(I)V");
		if (NULL == hotKeyCallbackMethodId) return;
		if (lpMsg.message== WM_HOTKEY){
			jstring result = (jstring)(*env)->CallObjectMethod(env, callbackObject, hotKeyCallbackMethodId, lpMsg.wParam);
		}
	
	}

}
复制代码

Cette partie du code est la plus importante. Tout d'abord, enregistrez 4 raccourcis clavier via la fonction RegisterHotKey, à savoir ←↑→↓, et donnez-leur un identifiant de 1, 2, 3 et 4, puis obtenez en continu des messages de la file d'attente de messages via GetMessage, GetMessage est généralement utilisé dans les applications de formulaire, et pour le moment, il n'y a pas de formulaire pour lui, donc le deuxième paramètre est NULL, et le premier paramètre est les informations de message arrivant dans la file d'attente. Lorsque le message est WM_HOTKEY, cela signifie Nous appuyons sur le raccourci clavier défini, obtenons l'adresse de rappel via l'API interactive fournie par Java et effectuons un appel.

En regardant l'implémentation de getDesktopIcon, qui est utilisé pour obtenir le nombre d'icônes du bureau, le bureau peut également être utilisé comme une fenêtre, et la liste des icônes sur le bureau est une ListView.Lorsque ListView reçoit le message LVM_GETITEMCOUNT, cela signifie que quelqu'un veut obtenir le nombre de ses icônes. , puis il nous reviendra, et Windows fournit la fonction SendMessage, qui est utilisée pour envoyer un message à la fenêtre spécifiée, et a une valeur de retour, et une fonction PostMessage pour aucune valeur de retour, qui sera décrite ci-dessous.

int GetDesktopIconCount() {
	return  SendMessage(GetWindowHwnd(), LVM_GETITEMCOUNT, 0, 0);
}
JNIEXPORT jint JNICALL Java_com_h_game_jni_GameJNI_getDesktopIcon
(JNIEnv *env, jobject thisObj) {
	return GetDesktopIconCount();
}
复制代码

Pour obtenir le handle de ListView sur le bureau, vous devez le faire, mais je ne sais pas si ce code peut s'exécuter normalement sur Win11, car la structure du bureau dans chaque système peut être différente, Win7 et Win10 sont différents, si Win11 Sans modifier cette structure, ce code fonctionnera correctement.

int GetWindowHwnd() {
	HWND hwndWorkerW = { 0 };
	hwndWorkerW = FindWindow(NULL,TEXT("Program Manager"));
	hwndWorkerW = FindWindowEx(hwndWorkerW, 0, TEXT("SHELLDLL_DefView"), NULL);
	hwndWorkerW = FindWindowEx(hwndWorkerW, 0,TEXT("SysListView32"), TEXT("FolderView"));
	return hwndWorkerW;
}

复制代码

De nombreux points de connaissance sont impliqués ici, la poignée et la poignée de recherche. La poignée est un nombre entier, qui est utilisé pour identifier une fenêtre d'application unique. La poignée de recherche peut être recherchée en fonction du nom de la classe ou du nom du titre de la fenêtre, ce qui inclure la relation enfant-parent. Pour plus de détails, vous devez comprendre la fonction CreateWindow.

Voici l'implémentation de l'icône mobile. Lorsque ListView reçoit le message LVM_SETITEMPOSITION, il définit la position en fonction d'autres paramètres. Ici, PostMessage est utilisé pour transmettre le message car nous n'avons pas besoin de valeur de retour. Si SendMessage est utilisé , ce sera beaucoup plus lent.

void MoveDesktopIcon(int iconIndex,int x,int y) {
	PostMessage(GetWindowHwnd(), LVM_SETITEMPOSITION, iconIndex, ConversionXY(x, y));
}
int ConversionXY(int  x, int  y) {
	return  y * 65536 + x;
}
JNIEXPORT void JNICALL Java_com_h_game_jni_GameJNI_moveDesktopIcon
(JNIEnv *env, jobject thisObj, jint index, jint x, jint y) {
	MoveDesktopIcon(index, x, y);
}


复制代码

Eh bien, ce qui suit est l'implémentation du code Java.

public class Main {
    static {
        System.load("Game.dll");
    }

    public static void main(String[] args) {
        new SnakeGame();
    }
}
复制代码

Cette partie est relativement simple, il n'y a rien à dire, le fonctionnement de base de l'icône a été fourni par c, et on peut l'appeler directement ici.

Notez qu'ici nous prenons l'index d'icône 0 comme tête, et le dernier index comme nourriture, par exemple, il y a 10 icônes, 0 est la tête, 9 est la nourriture, 1-8 est le corps, chaque fois que vous déplacez le tête selon la direction, enregistrez d'abord le mouvement avant la position, lorsque la tête bouge, passez sa position enregistrée à la première icône, puis passez la position d'origine de l'indice 1 à la seconde, et ainsi de suite, formant un serpent gourmand.

public class SnakeGame extends JFrame implements HotkeyCallback {
    private static final int WINDOW_WIDTH = 300;
    private static final int WINDOW_HEIGHT = 200;
    private GameJNI gameJNI = new GameJNI();
    private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
    private Direction direction = Direction.RIGHT;
    private Point snakePoint = new Point(0, 0);
    private static final int MOVE_SIZE = 84;
    private List<Point> snakeBodyPoint = new ArrayList<>();
    private Point foodPoint = new Point(0, 0);
    private List<Point> allPoint = generatorPoints();
    private ScheduledFuture<?> scheduledFuture = null;

    public SnakeGame() {
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        this.setLocation(screenSize.width / 2 - WINDOW_WIDTH / 2, screenSize.height / 2 - WINDOW_HEIGHT / 2);
        this.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        init();
        this.setVisible(true);
    }

    private void startGame() {
        this.setVisible(false);
        if (scheduledFuture == null) {
            scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {
                Point oldPoint = snakePoint.getLocation();
                if (direction == Direction.LEFT) snakePoint.x -= MOVE_SIZE;
                if (direction == Direction.UP) snakePoint.y -= MOVE_SIZE;
                if (direction == Direction.RIGHT) snakePoint.x += MOVE_SIZE;
                if (direction == Direction.DOWN) snakePoint.y += MOVE_SIZE;
                moveSnakeHeader();
                moveSnakeBody(oldPoint);
                isCollision();
            }, 0, 200, TimeUnit.MILLISECONDS);
        }

    }


    private void isCollision() {
        if (snakeBodyPoint.stream().anyMatch(point -> point.equals(snakePoint))) {
            scheduledFuture.cancel(true);
            scheduledFuture = null;
            this.setVisible(true);
        }
    }

    private void moveSnakeHeader() {
        gameJNI.moveDesktopIcon(0, snakePoint.x, snakePoint.y);
        if (foodPoint.equals(snakePoint)) resetBodyLocation();
    }

    private List<Point> generatorPoints() {
        List<Point> all = new ArrayList<>();
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        for (int i = 0; i < screenSize.width; i += MOVE_SIZE) {
            for (int j = 0; j < screenSize.height; j += MOVE_SIZE) {
                all.add(new Point(i, j));
            }
        }
        return all;
    }

    private void resetBodyLocation() {
        List<Point> newPoint = allPoint.stream().filter(point -> !has(point)).collect(Collectors.toList());
        Collections.shuffle(newPoint);
        Point point = newPoint.get(0);
        int desktopIcon = gameJNI.getDesktopIcon();
        foodPoint.setLocation(point.x, point.y);
        gameJNI.moveDesktopIcon(desktopIcon - 1, point.x, point.y);
    }

    private boolean has(Point hasPoint) {
        return snakeBodyPoint.stream().anyMatch(point -> hasPoint.equals(point));
    }

    private void moveSnakeBody(Point oldPoint) {
        for (int i = 1; i < snakeBodyPoint.size() - 1; i++) {
            Point itemPoint = snakeBodyPoint.get(i);
            gameJNI.moveDesktopIcon(i, oldPoint.x, oldPoint.y);
            snakeBodyPoint.set(i, oldPoint.getLocation());
            oldPoint = itemPoint;
        }
    }


    private void init() {
        this.setLayout(new BorderLayout());
        String str ="<html>首先右击桌面,查看>取消自动排列图片、将网格与图片对齐。方向键为← ↑ → ↓</html>";
        JLabel jLabel = new JLabel(str);
        jLabel.setFont(new Font("黑体",0,18));
        add(jLabel, BorderLayout.NORTH);
        add(createButton(), BorderLayout.SOUTH);
        registerHotkey();
        reset();
    }

    private void reset() {
        snakeBodyPoint.clear();
        direction = Direction.RIGHT;
        snakePoint.setLocation(0, 0);
        int desktopIcon = gameJNI.getDesktopIcon();
        int offsetX = -MOVE_SIZE;
        for (int i = 0; i < desktopIcon; i++) {
            snakeBodyPoint.add(new Point(offsetX, 0));
            gameJNI.moveDesktopIcon(i, offsetX, 0);
            offsetX -= MOVE_SIZE;
        }
        resetBodyLocation();
    }

    private JButton createButton() {


        JButton jButton = new JButton("开始");
        jButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                reset();
                startGame();
            }
        });
        return jButton;
    }

    @Override
    public void hotkey(int key) {
        if (key == 1) direction = Direction.LEFT;
        if (key == 2) direction = Direction.UP;
        if (key == 3) direction = Direction.RIGHT;
        if (key == 4) direction = Direction.DOWN;

    }

    public void registerHotkey() {
        new Thread(() -> gameJNI.registerHotkeyCallback(this)).start();
    }

    enum Direction {
        LEFT, UP, RIGHT, DOWN
    }
}
复制代码

Je suppose que tu aimes

Origine juejin.im/post/7084865137496031263
conseillé
Classement