まるぼ実験場

アプリの開発日記を載せるサイトでしたが、ただの技術ブログになりました。

どこかで見たコマンドを延々とタイピングするタイピングゲームを作った

 

3ka.me

突然ですが、ミニゲームを作ってみました。

製作の動機や元ネタとしては、

marubodiary.hateblo.jp

「自作したアケコンでなんかやりたいなー、

そうだ!格ゲーのコマンドをタイピングするゲームを作ろう!コマンドの練習になりそう!」

……でChatGPTに先行研究させていたときの産物がこちらだったのですね。

marubodiary.hateblo.jp

これをベースに(それはもう跡形もないくらいに)改造してできたのが今回のゲーム、という。
やっぱマニアックな仕様を入れたいというか、複雑になるとChatGPTのプロンプトだけでやるのは難しそうでしたね。

今回は全て生のJavaScriptで作っています。

実装で苦労したところ

実際の格ゲーにある仕様として、実際にされた入力を補助するような、簡易入力という機能があるのですが、それの判定がちょっとたいへんでした……。

アケコン自作してないと思いつかなかったであろう箇所として、隣接する入力をビット演算で処理してるところ。結構気に入ってる。

後は、JavaScriptのキーボード入力ってKeyPushとKeyReleaseで2回入るので、そのイベントの処理で考える事がありましたね。

例えば↘の斜め入力があるとき、→(KeyPush)↓(KeyPush)が入ったことが判定されてから初めて↘が入るので、無視しないとミス扱いになってしまうところとか。(Release時も同様)

一回転コマンドは途中で斜めの入力が入った場合でも許容するようにしているとか。

(とあるコマンドに至っては、専用の処理を入れて対策しているという……。原作ではどうなってるんだろう??)

なので実際のゲームより入力の判定は大分甘め?かもしれないです。(まぁ入力の時間制限もないし)

その辺はソースでまんま読めるので気になったら覗いてみてほしい。

筆者がそんなに格ゲーやらないので、ここの判定は実際と比べて挙動がおかしい!とかあったら指摘して貰えるとありがたいです。

 

【Python】VTuberアプリソースコード【2024/05/12更新】


何の変哲もないアプリで遅くなりましたが公開いたします!
サムネもなんとなくYouTube風。

VTuberって、最近では手軽に参入できるアプリが出来はじめたものの、自分オリジナルのキャラを使いたい!と思うとすごーく手間がかかるってイメージですよね。

まずキャラクターをデザインして…
3Dだったらモデリングして、モーションキャプチャーして…
2Dでもグラフィックを細かくパーツ分けして…
配信を賑やかにしたいけどちょっとそこまでは厳しい…って思いながらひそかにバーチャルアバター配信を見て(最低限どんな動作があればぎりぎりそれっぽく見えるのか)研究しつつ、考えたものがこちら。

『なんちゃってVTuber』基本仕様

・2Dイラストのみ対応
・一定時間ごとにイラストが目パチ(瞬き)する
・マイク入力に合わせて口パクする

これだけ。カメラも使わない音声検知だけ簡単なんちゃってVTuber

なんか…どっかで見たような絵面だなと思ってたらゆっくりだこれ。
…これでVTuberなんて言ったらファンに怒られますかね?

でもやっぱり、自分の動画内でキャラクターが反応してくれるって面白いね…口パクと目パチだけでだいぶ印象違う。リアルタイム感が出た気がします。

技術的な話。

動作ツールはPython、使用した画像も顔のベース、目と口それぞれ二種、目と口より上に来るパーツの透明情報入り画像たった6枚だけ。

ウィンドウ画面表示に関しては、
OpenCVを使うかPygameにするか悩んだのですが、FPSの調整とかがやりやすそうだなと思いPygameを使用。
将来的に凝った画像加工や認識を使いたいならOpenCVにする方がいいかもしれない。
 
というか今の仕様なら画像の透明部分重ねて表示できればなんでもいい。

音声認識に関してはこちらを参考にしました。
qiita.com
基本的な流れとしては、Pygameの1ループに一回音声を検知して、入力があったら画像を切り替えるだけの雑な設定。

更新履歴

2021/05/30 初版
2024/05/03 不具合修正版ソースコードを初公開
2024/05/12 全体的に記事を修正。素材用画像差し替え。

※当ソースコードを用いて発生した問題について、管理人は責任を負いかねます。ご了承ください。

使い方

1. Pythonを実行できる環境を作る
2.素材をダウンロードする
ダウンロードリンクはこちら。
https://3ka.me/sozai/nantyatteV.zip

3. コマンドプロンプトを起動し、ファイルを置いたディレクトリにcdで移動し『python main_v02.py』で実行

同梱の画像はブログ筆者がフリー素材として手描きしたものなので、ご自由にお使いください。
前のサンプル画像(記事サムネで使ってるキャラ)は当時適当に描いたもので、今見たら少し恥ずかしかったので改めて新キャラクターで描き直しました。
改変などもご自由にどうぞ(zipファイルの中にクリスタ形式のファイルも同梱しております)。

画像の構造は、
base.png

kuti.png

kuti2.png

me.png

me2.png

me3.png

add.png(※全て透明色の画像)

こんな感じで重ね合わせる前提でパーツ分けされてますが、ちょっとソースいじったら通常ととじ目、口開けの3枚だけでも
何とかなると思います。
これなら、https://character-nantoka.appspot.comとかの画像も使えますね。
今回画像については将来機能拡張することを考えていますので、細かめに。

ソースコード

2024/05/12 更新版

import pygame
from pygame.locals import *
import sys
import pyaudio
import numpy as np
import threading

#pyaudio初期化
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100

p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,channels = CHANNELS, rate = RATE, input = True, frames_per_buffer = chunk)
# 閾値
threshold = 0.025
# 音データ格納用配列
x = np.empty(chunk)

#pygame初期化
pygame.init()
screen = pygame.display.set_mode((500,500))
pygame.display.set_caption("VTuber?")

#画像取得
#基本
bg = pygame.image.load("Vtuber_base.png").convert_alpha()
image_rect = bg.get_rect()
#目
me1 = pygame.image.load("Vtuber_me.png").convert_alpha()
me2 = pygame.image.load("Vtuber_me2.png").convert_alpha()
me3 = pygame.image.load("Vtuber_me3.png").convert_alpha()
#口
kuti1 = pygame.image.load("Vtuber_kuti.png").convert_alpha()
kuti2 = pygame.image.load("Vtuber_kuti2.png").convert_alpha()
#その他
add = pygame.image.load("Vtuber_add.png").convert_alpha()
#フォント
font = pygame.font.Font(None, 20) 
#タイマー変数
Timer = 0

#描画周期設定
Clock = pygame.time.Clock()
FPS = 30

	
def draw():
	while True:
		pygame.display.update()
		
		#時間経過
		Timer = pygame.time.get_ticks()
		# 画像生成
		ImageOut(screen, image_rect, bg, Select_Eye(Timer), Select_mouse(x, threshold), Select_Add())
		
		#デバッグ用
		#DebugGraph(screen, Timer)

		pygame.display.flip()
		Clock.tick(FPS)                     #画面更新

def streamread():
	global x
	while True:
		# 音データの取得
		data = stream.read(chunk)
		
		# ndarrayに変換
		x = np.frombuffer(data, dtype="int16") / 32768.0


def main():
	while True:
	# 終了用のイベント処理
		for event in pygame.event.get():
			# 閉じるボタンが押されたときの処理
			if event.type == QUIT:
				stream.close()
				p.terminate()
				pygame.quit()
				sys.exit()

#----------------
# パーツ選択
#----------------
def Select_Eye(Timer):
	
	# 瞬き(力技。。。もうちょい自然な見せ方があるはず
	if ((Timer % 2100) <= 160):
		put_me = me2        #目(閉じ)描画
	else:
		put_me = me1        #目描画
	
	#キー押下によって目の描画オブジェクトを変更する
	pressed_key = pygame.key.get_pressed()
	if pressed_key[K_q]:
		put_me = me3            #目(><)描画
	
	return put_me

def Select_mouse(x, threshold):
	#音声認識によって口の描画オブジェクトを変更する
	if x.max() > threshold:
		put_kuti = kuti1      #音声入力があるときの口描画
	else:
		put_kuti = kuti2      #何もないときの口描画
		
	return put_kuti
	
def Select_Add():
	#一番上に描画する画像(アクセサリーなど)
	put_add = add
	
	return put_add
	
#----------------
# 描画
#----------------
def ImageOut(screen, image_rect, bg, me, kuti, add):
	screen.fill((0, 255, 0, 0))         # 画面の背景色(緑)
	screen.blit(bg, image_rect)         # ベース画像の描画
	screen.blit(me, image_rect)     # 目画像の描画
	screen.blit(kuti, image_rect)   # 口画像の描画
	screen.blit(add, image_rect)     # さらにその上に重なる画像の描画
	
	return

#----------------
# デバッグ用表示
#----------------
def DebugGraph(screen, Timer):
	print(Timer)
	
	return

#スレッド作成
thread1 = threading.Thread(target=draw)
thread2 = threading.Thread(target=streamread)
thread1.setDaemon(True)
thread2.setDaemon(True)
thread1.start()
thread2.start()

if __name__ == "__main__":
	main()

使い方

サンプル素材の画像を使用して実行したウィンドウ。

時間や音声の条件を元にばらばらだった画像が合成されるので、それで出力されたウィンドウをOBSに設定するだけ!

アプリで合成したウィンドウの使用例。
…ただ特筆したような機能はないし、作ろうと思えば上記情報をもとにすぐ作れると思うので、ちょっとしたネタにいかがですか!?
やろうと思えばキャラをもっと動かしたり音声の音量で表情パターン変えたりとかいろいろできそうですね。…あんまり凝りすぎてももうそれLive2Dで良くね?ってなりそうですが!

今後の展望

このプログラム、問題点は音ゲー配信と相性が悪いこと。
マイクが鍵盤の音を拾っちゃってそれで常に口パクしてる(うざい)という問題が発覚し、プレー配信時の合成画像は口パクのコードを削除した状態で撮った。
単指向性マイクにしたり音域カットしたりしたらマシになるのかもしれない。
・マイク感度の改善
・外部デバイスとの連携
・起動してるとタスクトレイのマイクが荒ぶる問題の解決(←大問題じゃないか
※2024/05/03 追記
解決しました。


いろいろと拡張できる題材だと思うので、こんな機能つけてみたよ!とか色々拡張して遊べると思います。

余談

最初Markdown記法でソース貼ろうかと思ったんだけど、
コメントの#に反応していちいち見出しになってたのね…
なので今回の記事ははてな記法で書いています。

Pyaudioをループしたときに通知領域のマイクアイコンが荒ぶっていた問題が今更解決した

marubodiary.hateblo.jp
この大昔作ったネタアプリだが、最後に大問題を残したままなのだった。
・起動中に通知領域のマイクが荒ぶる問題
これの解決にようやく取り組む気になってそして解決したので、問題解決版のソースコードを書いておく。

結論

音声入力と画像描画を、スレッドに分けて処理した。

import pygame
from pygame.locals import *
import sys
import pyaudio
import numpy as np
import threading

#pyaudio初期化
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100

p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,channels = CHANNELS, rate = RATE, input = True, frames_per_buffer = chunk)
# 閾値
threshold = 0.025
# 音データ格納用配列
x = np.empty(chunk)

#pygame初期化
pygame.init()
screen = pygame.display.set_mode((500,500))
pygame.display.set_caption("VTuber?")

#画像取得
#基本
bg = pygame.image.load("base.png").convert_alpha()
image_rect = bg.get_rect()
#目
me1 = pygame.image.load("me.png").convert_alpha()
me2 = pygame.image.load("me2.png").convert_alpha()
me3 = pygame.image.load("me3.png").convert_alpha()
#口
kuti1 = pygame.image.load("kuti.png").convert_alpha()
kuti2 = pygame.image.load("kuti2.png").convert_alpha()
#髪
kami = pygame.image.load("kami.png").convert_alpha()
#フォント
font = pygame.font.Font(None, 20) 

#タイマー変数
Timer = 0

#描画周期設定
Clock = pygame.time.Clock()
FPS = 30

	
def draw():
	while True:
		pygame.display.update()
		
		#時間経過
		Timer = pygame.time.get_ticks()
		# 画像生成
		ImageOut(screen, image_rect, bg, Select_Eye(Timer), Select_mouse(x, threshold), Select_Add())
		
		#デバッグ用
		#DebugGraph(screen, Timer)

		pygame.display.flip()
		Clock.tick(FPS)                     #画面更新

def streamread():
	global x
	while True:
		# 音データの取得
		data = stream.read(chunk)
		
		# ndarrayに変換
		x = np.frombuffer(data, dtype="int16") / 32768.0


def main():
	while True:
	# 終了用のイベント処理
		for event in pygame.event.get():
			# 閉じるボタンが押されたときの処理
			if event.type == QUIT:
				stream.close()
				p.terminate()
				pygame.quit()
				sys.exit()

#----------------
# パーツ選択
#----------------
def Select_Eye(Timer):
	
	# 瞬き(力技。。。もうちょい自然な見せ方があるはず
	if ((Timer % 2100) <= 160):
		put_me = me2        #目(閉じ)描画
	else:
		put_me = me1        #目描画
	
	#キー押下によって目の描画オブジェクトを変更する
	pressed_key = pygame.key.get_pressed()
	if pressed_key[K_q]:
		put_me = me3            #目(><)描画
	
	return put_me

def Select_mouse(x, threshold):
	#音声認識によって口の描画オブジェクトを変更する
	if x.max() > threshold:
		put_kuti = kuti1      #音声入力があるときの口描画
	else:
		put_kuti = kuti2      #何もないときの口描画
		
	return put_kuti
	
def Select_Add():
	#一番上に描画する画像(アクセサリーなど)
	put_add = kami      #髪描画
	
	return put_add
	
#----------------
# 描画
#----------------
def ImageOut(screen, image_rect, bg, me, kuti, add):
	screen.fill((0, 255, 0, 0))         # 画面の背景色(緑)
	screen.blit(bg, image_rect)         # ベース画像の描画
	screen.blit(me, image_rect)     # 目画像の描画
	screen.blit(kuti, image_rect)   # 口画像の描画
	screen.blit(add, image_rect)     # 上部画像の描画
	
	return

#----------------
# デバッグ用表示
#----------------
def DebugGraph(screen, Timer):
	print(Timer)
	
	return

#スレッド作成
thread1 = threading.Thread(target=draw)
thread2 = threading.Thread(target=streamread)
thread1.setDaemon(True)
thread2.setDaemon(True)
thread1.start()
thread2.start()

if __name__ == "__main__":
	main()

ChatGPTを使って一瞬で作ったJavaScriptタイピングゲーム

久しぶりにChatGPT使ったら応答がすげー早くなっててびっくりした。
次回作るゲームのたたき台にするためにこのコードを書いて貰ったんだけど、これだけでも結構遊べる気がした。日本語で指示しただけでこれができるとは、すごい……。
AIがイラストの方面で物議を醸しているところで、AIが作成したソースコードを丸々載せるって著作権的にはどうなんだろう……と思うところはあるんだけども、
こんな感じで、なにかしら作る時のベースになるコードをサクッと出させるのにAIを活用するのは良いなぁと思っていたり。

主に指示した仕様
JavascriptCanvasに描画
・方向キーも入力に使えるようにする
・お題を全20題からランダムに設定
・途中で入力に失敗した場合、最初からやり直しにさせる
・クリアしたらお題に手をつけてからクリアするまでの経過秒数をCanvas内に0.5秒間表示してから次のお題を出す

指示してない機能のコードを削らせることにちょっと気を遣ったかもしれない(^_^;

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Typing Game</title>
<style>
    canvas {
        border: 1px solid black;
    }
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="200"></canvas>
<script>
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');

    const topics = ['apple', 'banana', 'cherry', 'orange', 'grape', 'melon', 'pear', 'strawberry', 'watermelon', 'pineapple', 'kiwi', 'mango', 'blueberry', 'peach', 'apricot', 'plum', 'fig', 'coconut', 'lime', 'lemon'];
    let currentTopicIndex = -1;
    let currentTopic = '';
    let inputText = 'input: ';
    let userInput = '';
    let startTime = null;

    function chooseRandomTopic() {
        currentTopicIndex = Math.floor(Math.random() * topics.length);
        currentTopic = topics[currentTopicIndex];
        userInput = '';
        startTime = new Date().getTime();
        draw();
    }

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.font = '20px Arial';
        ctx.fillText(inputText + currentTopic, 10, 30);
        ctx.fillText(inputText + userInput, 10, 60);
        if (userInput === currentTopic) {
            const elapsedTime = (new Date().getTime() - startTime) / 1000;
            ctx.fillText(`Clear! Time: ${elapsedTime.toFixed(1)} seconds`, 10, 90);
        }
    }

    function handleInput(event) {
        const key = event.key;
        const keyCode = event.keyCode;

        if (key.length === 1) {
            userInput += key;
        } else if (keyCode >= 37 && keyCode <= 40) { // Arrow keys
            userInput += handleArrowKeys(keyCode);
        }

        if (!currentTopic.startsWith(userInput)) {
            userInput = '';
        }

        if (userInput === currentTopic) {
            setTimeout(() => {
                chooseRandomTopic();
            }, 500); // 0.5秒後に次のお題に進む
        }

        draw();
    }

    function handleArrowKeys(keyCode) {
        switch (keyCode) {
            case 37: // Left arrow
                return '←';
            case 38: // Up arrow
                return '↑';
            case 39: // Right arrow
                return '→';
            case 40: // Down arrow
                return '↓';
            default:
                return '';
        }
    }

    document.addEventListener('keydown', handleInput);

    chooseRandomTopic();
</script>
</body>
</html>

サンプル
3ka.me

『パーフェクトPHP』Part3 6章のコードをSQLite仕様に書き直してみる

パーフェクトPHPは良い本なのだが、少々内容が古くなってしまっているため、調べながら読むのが良い。
6章Part3のひとこと掲示板のコードは、例示されているmysql関数が非推奨なので書き換える必要がある。
ついでに、SQLite3クラスを使う仕様に書き直してみた。コメントアウト部分は元コードを現在標準のmysqli関数仕様に書き直したもの(未テスト)
これは筆者が以前にPythonでWebアプリ作ったとき、MySQLを入れてから面倒なことになったので、メインのPC(Windows環境)で開発テスト段階のときはSQLiteを使うようにしているから。(規模が大きくなって必要になり次第、Linux環境のほうに別途環境を構築するようにしている。)
# 書籍からSQL文などの箇所を流用する形で書きたかったのでこうなったが、現代ではPDOを使う様にしたほうが流用しやすそうと思う。

(2024/04/07 ソースコードの間違いとインデントを見やすく修正。ついでにステートメント版を追加。)

データベース接続

//データベースに接続
/*
$link = mysqli_connect('localhost', 'root', '');
if(!$link){
    die('データベースに接続できません:'. mysqli_connect_error());
}

//データベースを選択する
mysqli_select_db($link, 'onbline_bbs');
*/
    
try{
    $link = new SQLite3('hitokoto.db');
}catch(Exception $e){
    die('データベースに接続できません:');
}

SQL作成・保存

なるべく書籍に近い書き方で。

//エラーが無ければ保存
if(count($err_msg) === 0){

 //SQL文作成
$sql = "INSERT INTO 'post' ('name' , 'comment', 'created_at') VALUES ('"
           //. mysqli_real_escape_string($link, $name) . "','"
              . $link->escapeString($name). "','"
           //. mysqli_real_escape_string($link, $comment) . "','"
              . $link->escapeString($comment) . "','"
              . date('Y-m-d H:i:s') . "')";
            
//保存する
//mysqli_query($link, $sql);
$link->query($sql);

//mysqli_close($link);
$link->close();

header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
}

実際にはステートメント使う方が多いのかな?ということでステートメント使用版。

$sql = "INSERT INTO 'post' ('name' , 'comment', 'created_at') VALUES ( :name, :comment, :date )";
$date = date('Y-m-d H:i:s');

$stmt = $link->prepare($sql);
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$stmt->bindParam(':comment', $comment, SQLITE3_TEXT);
$stmt->bindParam(':date', $date, SQLITE3_TEXT);

//保存する
$stmt->execute();

$link->close();

HTMLの表示用要素取得部分

 // 投稿された内容を取得
$sql = "SELECT * FROM 'post' ORDER BY 'created_at' DESC";
//$result = mysqli_query($link, $sql);
$result = $link->query($sql);

$posts = array();
//if($result != false && mysqli_num_rows($result)):
     //while ($post = mysqli_fetch_assoc($result)){
if($result !== false && $result->numColumns()){
    while($post = $result->fetchArray()){
       $posts[] = $post;
    }
}
            
//取得結果を開放して接続を閉じる
//mysqli_free_result($result);
//mysqli_close($link);
$link->close();

余談

スタードメイン(無料サーバー)上でSQLiteが使えるかを上記コードにした状態でテストしてみたところ、
書き込み、読み込み共に問題無さそうだった。
夢が広がるね。

諭吉2枚中古ノートパソコンで遊ぶ ~Linuxによるデュアルトリプルブート~

ここからLinux転生編がスタート。

marubodiary.hateblo.jp

前回記事は中古パソコンを買ってちょっとカスタマイズした記録まで。

今回はLinuxをインストールしていきますが、一応まだサポート残ってるWindows10があるので残しておこうかなと。なるべく安全に(?)WindowsLinuxデュアルブート機にすることが今回の目標です。

基本的に同じストレージ内に複数OSを入れるデュアルブートはトラブルへの見舞われやすさ諸々を考えてあんまりやるべきではないのだが、こうやって色々実験するのに格安中古パソコンはうってつけってわけよ。

いやぁ、どこかの会社で要らなくなって市場に出されたと思われる元高級ノートパソコンでこうやって遊べるなんて、追放モノラノベみたいで楽しくなってきますねぇ!(何言ってんだこいつ)Linuxに転生してこれからも大活躍するんだ!

1.Linux mintをインストール

最初にインストールするOSはLinux mintにします。

無難にUbuntuのが良くない?Ubuntuはなんか飽きた。という理由でmintになりました。

f:id:kikyou_kiki:20231221104316j:image
まずはGpartedでLinuxの領域を確保します。

インストーラーでもその辺はいじれますが、それよりも使いやすくて分かりやすいパーティション管理ツールなのでこっち使うのがオススメ。

Windows10 約180GB

Linux mint 約80GB

にしておきました。

スワップ領域は……SSDなので無くてもいいかな……?


領域決めたらデスクトップにあるアイコンを押してインストールしていきます。

ex.Linuxブートローダーどうするの問題

ブートローダーはパソコンの電源を入れるとBIOS(UEFI)から呼ばれ、OSをブートするプログラム。(ざっくり説明)

インストール時にブートローダーの場所を指定する必要があるが、この選択はかなり重要だったりする。

特に何も指定しない場合、ディスクの先頭のEFIパーティションブートローダーをインストールしようとする。

OSを一つしか入れない状態ならもちろんそれでも問題ないのだが、Windowsがインストールされている所謂デュアルブート環境だと問題が発生する可能性がある。

Windowsがインストールされている環境では、この場所には既にWindowsブートローダー(Windowsブートマネージャ)が入っている。

この状態でLinuxブートローダーをインストールした場合、Windowsブートマネージャは消えるわけでは無いが、起動時に呼ばれるブートローダーがLinuxブートローダー(GRUB2)に置き換えられて、そこからWindowsブートマネージャを呼ぶ形に変更される。

一応それでもちゃんとデュアルブートは出来るが、Windows側がデュアルブートであることを認識しない(というか推奨していない?)ため、Linuxに構わずブートローダー部分をアプデで上書きしてしまうことがある。


ブートローダー書き換えによってOSが起動出来なくなるなど、かなり面倒いことになる可能性が考えられる。

なので、Linuxブートローダーはなるべく独立させたいのと、Windowsブートローダーはなるべく維持しておきたいのです。


f:id:kikyou_kiki:20231221104425j:image

f:id:kikyou_kiki:20231221104445j:image
ということでインストールの種類を「それ以外」、ブートローダーはWindowsブートローダーがある場所とは別の場所にインストールする方法にしたいと思います。

今回は、Linux mintは「/dev/sda4」、ブートローダーはLinuxになるパーティションの先頭にインストールするように指定しインストール。

f:id:kikyou_kiki:20231221104527j:image
再起動するとLinux mintブートローダー(GRUB2)のメニューが起動します。


これで完了……かと思いきや。

調べてみるとブートローダーの場所がおかしい。

f:id:kikyou_kiki:20231221104544j:image

dev/sda2、Windowsブートローダーの場所では……?ちゃんと/dev/sda4にしたはずなのに……どうして……

普通に使う分だと気にならないかもですが、筆者は気にしてるのでもうちょっと調べてみます。

f:id:kikyou_kiki:20231221104556j:image
Windows上から調べた。……思いっきり同居してますやん。

 

【Ubuntu】【上級者向け】Grubブートローダを削除する – 茱萸note

一旦別のLinux(あるいは先ほど作成したUSBのLinux)からブートローダーの情報を削除してからもう一度やり直してみる。


ちなみにこの間割愛するけど、GRUBの再インストールコマンドとかブートリペアツールやら別OSからごにょごにょとかも使ってみたけど何をやってもブートローダーがdev/sda2に戻ってきた様子だったよ、なんでや。


色々調べてみると、ブートローダーは先頭2TBまでのfat32フォーマットの領域からでないとUEFIが読み出せないという記述を見つけたので、

パーティションの切り出しからOSインストールをもう一回やり直す事にしました。

f:id:kikyou_kiki:20231221104755j:image
今度は専用のブート領域を作ってそこにインストールするよう指定する……。

f:id:kikyou_kiki:20231221104829j:imagef:id:kikyou_kiki:20231223095808j:image
……????


は????

 

Ubuntu 22.04 その191 - UEFIのブートローダーが選択されたデバイスにインストールされない不具合 - kledgeb

どうやら不具合があったらしい。
今のmintのバージョンはUbuntu22.04なので、もろですやん……。

との事で上記記事の対処法、インストール前にGpartedでWindowsブートローダー領域のフラグから「esp、boot」などのフラグを外してから専用のEFI領域にインストール。

再起動後はブートメニューが飛ばされてそのままLinux mint起動。(先ほどフラグを変えたため、他ブートローダーにいるWindowsの存在が認識されていないと思われる)


f:id:kikyou_kiki:20231223095821j:image

今度は成功したみたい。

でも今後のアップデートとかで戻ってきそうでコワイとこ。

(今更だけど、パーティション先頭にブートローダーかつEFIフラグオフでのインストールも試してみれば良かったかなぁ……)

2.manjaroをインストール

mintの項目が長くなりましたが、トリプルブートチャレンジ。

先ほどと同じくUSBにセットアップディスクを作ってブート……する前に、UEFIの設定でセキュアブートをが無効になってることを確認。

そう、これがWindows11だと、Linuxの一部のディストリビューションデュアルブートしたいときに、どうしてもセキュアブート対応で問題が出てくる。

そのためにWindows10の中古を選んでいた所がある。メインのパソコンはさっさと11にしてしまったのもあってね……。今後に期待。


f:id:kikyou_kiki:20231221104925j:image
さっきいじったブートローダーのフラグを元に戻して、残しておいた領域から80GBくらいをmanjaroで使うように設定する。

余りはNTFSでフォーマット……WindowsLinuxの両方から読めるデータ領域にするつもり。

それ以外の設定はウィザードに沿って進めていきます。

f:id:kikyou_kiki:20231221104936j:image

manjaroではブート領域を用意しないと怒られる。

f:id:kikyou_kiki:20231221104944j:image

さっきLinuxmintのブートローダーでだいぶん失敗したのでこうやって確認してくれるのは好印象。
manjaroはブートローダの場所も問題なく、すんなりインストール完了。f:id:kikyou_kiki:20231222021624j:image

再起動後のブートメニューには3つのOSがバッチリ載っていました。

最後をmanjaroにして本当に良かった……!(リアルにmintのブートローダー検証で役に立った)

f:id:kikyou_kiki:20231221105000j:image
Windowsからも確認。問題無さそうです。

3.ブートローダーをカスタマイズ(したかった)

ほんとはWindowsブートマネージャからLinuxを使えるようにしたかったのですが、設定するのはちょっと面倒そうなので最後にインストールしたmanjaroのブートローダーから使うようにしたほうが無難そう。

先ほど言ったように、後から入れたOSは既に入っているOSを検出してメニュー作ってくれているので、今後はmanjaro上からブートローダーの設定をするようにします。

順番とか表示時間とか。

https://w.atwiki.jp/linuxjapanwiki/pages/214.html#partition

Windowsブートマネージャを触る方法については、同じ手順やったけど、筆者の環境では失敗したのよね……(ブートマネージャにメニューは追加されるが起動出来ずエラー)

(ストレージがMBR形式のディスクなら、EasyBCDやMBMを使うのが綺麗でよさそう。)

まとめ

Windowsと色々なLinuxデュアルブートして遊びたいなら今のうちに10のパソコンを買うと楽かもしれない。

ブートローダ絡みで結構苦労した……その分仕組みとか復旧の仕方とか勉強になった。今はUbuntu(Linux mint)のインストーラーには注意しよう!!