OCI にminecraft用のサーバを立てる

OCI(Oracle Cloud Infrastracture)にminecraft鯖を立てていきます。
ここではありがたく無料枠を使用させていただき、Forgeの鯖を立てます。

前提

  • 以下ページからアカウント登録を済ませている
    OCI Cloud Free Tier
  • minecraft java版を所持している

目次

  1. OCIのインスタンスを立てる
  2. サーバーにファイルをアップロードしてみる
  3. コンソールからサーバーに接続してみる
  4. Forgeをインストール
  5. ポート穴あけ
  6. バックグラウンドで動かし続ける
  7. minecraftクライアントから接続確認

1. OCIのインスタンスを立てる

何はともあれインスタンスを立てます。

1-1. 左上のハンバーガーボタンから「コンピュート>インスタンス」の順に進む

1-2. 「インスタンスの作成」をクリック
クリックできない場合は左ペインのコンパートメントから「アカウント名(ルート)」を選択

1-3. 各種設定をする

「名前」はお好きに。minecraftを動かすサーバーなら「minecraft」でいいと思います。
「コンパートメントに作成」はデフォルトで。

「配置」はすべてデフォルト。「Always Free対象」と表示されていればOK。

イメージとシェイプのうち、
イメージは「イメージの変更」ボタンから「Canonical Ubuntu」に変更。
OSバージョンは最新版のうちminimalでないもの、イメージ・ビルドは最新版を選択。
おそらくUbuntuでなくてもサーバーは立てられますが、個人的に好きなのでUbuntuにしています。

Shapeは「Change Shape」ボタンから「シェイプ・シリーズ」を「Ampere」に変更。
Image: Canonical Ubuntu 22.04の下の表から「VM.Standard.A1.Flex」を選択。「Always Free対象」になっていればOK。
OCPU・メモリー量を選択します。最大で
OCPU : 4
メモリー量 : 24GB
まで行けます。minecraftにはオーバースペックですが、何も考えずに最大でもいいと思います。
最大にしなければ余りの分で別のインスタンスを立てることもできます。

ネットワーキングは、「プライマリ・ネットワーク」で「新規仮想クラウド・ネットワークの作成」を選択。
あとはデフォルトのままで良いですが、「新規パブリックサブネットの作成」「パブリックIPv4アドレスの割り当て」が選択されていることを確認してください。

SSHキーの追加では、忘れずに秘密鍵をダウンロードしておいてください。
作成後にダウンロードする方法は(おそらく)なさそうなのでダウンロードし忘れるとちょっと面倒です。

ブートボリュームはデフォルトのままで良いです。
そのまま「作成」ボタンをクリックしてインスタンスの作成は完了です。
しばらく(5分くらい?)待つとインスタンスが有効化され、アイコンが緑色になります。

2. サーバーにファイルをアップロードしてみる

うまくサーバーが起動したかの確認も兼ねてどんなファイルが入っているのか覗きつつ、ファイルをアップロードしてみます。
MacであればFileZillaやCyberDuckなどを使うとよいと思います。以下のスクリーンショットはFileZillaです。
Windowsはよくしらないです。WinSCPとかじゃないですかね。

2-2. サーバーのIPアドレスを確認する
OCIのWeb画面の「ハンバーガーボタン>コンピュート>インスタンス>表内にある先ほど作成したインスタンス」でインスタンスの詳細画面を表示します。
「パブリックIPアドレス」「ユーザー名」を確認します。

2-3. FileZillaでサーバーに接続
FileZillaに情報を入れていきます。他のソフトでも入力項目は大体同じ感じだと思います。
ホスト : OCIの画面で確認したパブリックIPアドレス
ポート : 22 (ダメそうなら空でもいけるはず)
ユーザー名 : OCIの画面で確認したユーザー名
鍵ファイル : インスタンスを作成したときにダウンロードした秘密鍵ファイル(ssh-key-XXXX-XX-XX.key)

「接続」をクリックすると、左側に自分のローカルファイル、右側に接続したサーバ上のファイルが表示されます。
おそらく右側は何もファイルが入っていない状態のファイルリストが表示されると思います。私はすでに色々入っているので色々表示されています。

2-4. ファイルをアップロード

お好きなバージョンのForgeインストーラを公式ページからダウンロードしておきます。
サーバー上に「Forge」ディレクトリを作って、ダウンロードしたインストーラを右ペインにドラッグ&ドロップするだけです。マジでこれだけです。

3. コンソールからサーバーに接続してみる

せっかくサーバを立ててファイルをアップロードしたのでsshで接続してみます。
この段階では22ポートしか開いていない(はず)ので、minecraftデフォルトの25565ポートには接続できませんが、ちょっとサーバーの様子を覗いてみます。

3-1. 接続用のシェルスクリプトを作成

作成といいつつ、まずは試しに接続してみます。
ターミナルで以下のコマンドを実行。Windowsは…わからん…PowerShell…?

$ ssh -i <秘密鍵ファイル> <ユーザー名>@<パブリックIPアドレス>

例えばこんな感じ。

$ ssh -i /home/toma/minecraft/ssh-key-2022-08-19.key ubuntu@135.11.2.3
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.13.0-1036-oracle aarch64)

 * Documentation: https://help.ubuntu.com
 * Management: https://landscape.canonical.com
 * Support: https://ubuntu.com/advantage

 System information as of Fri Aug 19 13:52:10 JST 2022
...

毎度毎度コマンドラインでIPアドレスと鍵ファイルを指定するのは面倒なのでシェルスクリプトを作成します。connect.shは鍵ファイルと同じフォルダに入れておくのがおすすめです。

#!/bin/bash
ssh -i <秘密鍵ファイル> <ユーザー名>@<パブリックIPアドレス>

これで次回から

$./connect.sh

で接続できます。

4.Forgeをインストール

Forgeをインストール…する前にJavaをインストールします。
この辺はバニラ鯖をインストールするのと大差ないです。

4-1. サーバーに接続して以下コマンドを実行
半分おまじないなのでとりあえず実行します。途中でなにか聞かれたら「y」と返しておきます。

$ sudo apt update
$ sudo apt upgrade

4-2. Java18をインストール
以下コマンドを実行します。ずらーっと色々表示され、インストールが完了します。

$ sudo apt install -y openjdk-18-jdk-headless


4-3. Forgeをインストール
以下コマンドでForgeをインストールします。
クライアントをインストールするときに表示されるようなものがずらっと表示され、インストールが完了します。

$ cd インストーラを置いたパス
$ java -jar forge-X.XX.X-XX.X.X-installer.jar --installServer

4-5. euraに同意

Forgeをインストールすると自動生成されるrun.shを一度実行します。
これがminecraftのサーバープログラムを起動するためのシェルスクリプトです。

$ ./run.sh

スクリプトが一旦終了して、eura.txtが自動生成されます。
適当なテキストエディタで「eura=false」を「eura=true」にします。

4-6. mod導入

バイオームが追加される系統のmodはここで導入しておきます。
インストーラを置いたディレクトリにmodsフォルダができているはずです。(なければ作ります)
そこにダウンロードしたmodを入れておきます。

4-7. サーバープログラムを実行
もう一度サーバープログラムを起動します。

$ ./run.sh

[ne.mi.co.MinecraftForge/FORGE]: MinecraftForge v40.1.0 Initialized
「Preparing spawn area: 0%」
などが表示されて、そのうち
Done (15.544s)! For help, type “help”

が出るはずです。
大丈夫そうであれば一旦サーバープログラムを停止します。

> stop

これでForgeのインストールは完了です。
メモリ使用量を変更したいときは適宜「user_jvm_args.txt」を書き換えます。

5. ポート穴あけ

minecraftクライアントからサーバにアクセスできるようにポートを開放します。

5-1. OCI側に穴を開ける

OCIのWeb画面でインスタンスの詳細画面に移動します。
サブネットをクリックします。

「Default_Security_List_for_vcn-XXXXXXXX-XXXX」をクリックします。

「イングレスルールの追加」ボタンから以下のルールを追加します。
「説明」はなんでも大丈夫です。dynmapを使用したい場合はここで宛先ポート8123も開けておくと良いです。

5-2. OS側に穴を開ける

OCIの設定でポート番号25564は通るようになりましたが、まだOS側ではふさがっている状態なので、そちらも開けていきます。

ターミナルからサーバにログインして「/etc/iptables/rules.v4」の「COMMIT(最終行)」より上に以下の行を追加します。

-A INPUT -p tcp -m state --state NEW -m tcp --dport 25565 -j ACCEPT

永続化させるため以下のコマンドを実行します。

$ sudo iptables-restore < /etc/iptables/rules.v4

一度サーバを再起動します。

$ reboot

ポート解放は以上です。これで大体サーバの設定は終わりになります。

6. バックグラウンドで動かし続ける

通常、プログラムは実行したユーザーがログアウトすると動作を停止します(Macもそうですね)(Windowsもそうですよね?)。ところが、ログアウトしても見えないところで動き続けてくれるようにしてくれるのがscreenコマンドです。
これにより、鯖主がターミナルをずっと開いていなくてもminecraftサーバープログラムが動き続けるようになります。

screenコマンドは仮想端末を起動するコマンドで、自分が操作している端末とは(仮想的に)別にバックグラウンドで動作します。操作のクセが強いですが、最低限 次の2つのオプションと1つの操作さえ覚えていればなんとかなります。
よくわからん!ってときはググってください。(放棄)

screenコマンドで仮想端末を作ったり 仮想端末に入ったりする

  • sceen -S <セッション名>      :  <セッション名>で新規仮想端末を開始&仮想端末に入る
  • screen -r <セッション名>      :  バックグラウンドで動いている<セッション名>に入る

仮想端末内の操作

  • ^A  D     : 現在の仮想端末から抜けて、バックグランドで動かしておく

minecraftサーバープログラムをバックグラウンドで動かす操作は大体次のイメージです。

ubuntu@minecraft:~/Forge$ screen -S Forge   #Forgeという名前の仮想端末を起動
----------ここから仮想端末内-----------
ubuntu@minecraft:~/Forge$ ./run.sh
------ここで^A Dで仮想端末から抜ける------
ubuntu@bedrock:~$ screen -S Forge 
[detached from 3743893.Forge]
ubuntu@bedrock:~$ logout
# あとはターミナル閉じる

7.minecraftクライアントから接続確認

最後にminecraftのクライアントでサーバーのアドレスを入力して接続してみましょう。つながればOKです。つながらなかったらこの記事が何か抜けています。ゆるして。


カテゴリ: minecraft
| OCI にminecraft用のサーバを立てる はコメントを受け付けていません

PhotoshopでUIBezierPath(2018/03/02版)

PhotoshopでUIBezierPathで作った時とSwiftの仕様がまた変わっているようなので新しいものをば。
多分今はSwift3.0? 新しいものがどんどん出てくるのでついていくのがちょっときついですね…^^;

とりあえずSwift3.0用に書き直してみました
それと、いまたまたま気づいたんですがPhothoshopの環境設定で「定規」の単位をpixelにしてないとうまく動きませんね…
何か回避する方法はあるのかしらん…

// Only run if we're in a supported version of Photoshop
// Note, removing this will not make the script work, as it depends on
// core application code, it just makes it fail better.
if (!app.featureEnabled("ImageStack Creation")) {
	throw (new Error(localize("$$$/JavaScripts/Statistics/CommandNotAvailable=The Statistics feature is not available in this version of Photoshop.")));
}
 
try{
	makePath();
}catch(err){
	alert(err);
}
 
function makePath(){
//	preferences.rulerUnits = Units.PIXELS;
	height = activeDocument.height.value
	width = activeDocument.width.value
	var str="//let height : CGFloat = "+String(height)+";\n";
	str += "//let width : CGFloat = "+String(width)+";\n";
	if(activeDocument.pathItems.length == 0){
		throw "パスを選択してください。";
	}
	for(var i=0; i<activeDocument.pathItems.length; i+=1){
		var activePath = activeDocument.pathItems[i];
		str += "let "+activePath.name+" : UIBezierPath = UIBezierPath();\n";
		for(var j=0; j<activePath.subPathItems.length; j+=1){
			for(var k=0; k<activePath.subPathItems[j].pathPoints.length; k+=1){
				var activePathPoints = activePath.subPathItems[j].pathPoints;
				if(k==0){
					str += activePath.name+".move(to: CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(	activePathPoints[k].anchor[1])+"));\r\n";
//					str += activePath.name+".move(to: CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(height-	activePathPoints[k].anchor[1])+"));\r\n";
				}else{
					str += activePath.name+".addCurve(to: CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(activePathPoints[k].anchor[1])+"),\r\n";
					str += "controlPoint1: CGPoint(x: "+String(activePathPoints[k-1].leftDirection[0])+", y: "+String(activePathPoints[k-1].leftDirection[1])+"),\r\n";
					str += "controlPoint2: CGPoint(x: "+String(activePathPoints[k].rightDirection[0])+", y: "+String(activePathPoints[k].rightDirection[1])+"));\n";
//					str += activePath.name+".addCurveToPoint(CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(height-activePathPoints[k].anchor[1])+"),\r\n";
//					str += "controlPoint1: CGPoint(x: "+String(activePathPoints[k-1].leftDirection[0])+", y: "+String(height-activePathPoints[k-1].leftDirection[1])+"),\r\n";
//					str += "controlPoint2: CGPoint(x: "+String(activePathPoints[k].rightDirection[0])+", y: "+String(height-activePathPoints[k].rightDirection[1])+"));\r\n";
				}
			}
				str += activePath.name+".addCurve(to: CGPoint(x: "+String(activePathPoints[0].anchor[0])+", y: "+String(activePathPoints[0].anchor[1])+"),\r\n";
				str += "controlPoint1: CGPoint(x: "+String(activePathPoints[activePath.subPathItems[j].pathPoints.length-1].leftDirection[0])+", y: "+String(activePathPoints[activePath.subPathItems[j].pathPoints.length-1].leftDirection[1])+"),\r\n";
				str += "controlPoint2: CGPoint(x: "+String(activePathPoints[0].rightDirection[0])+", y: "+String(activePathPoints[0].rightDirection[1])+"));\r\n";
		}
	}
	
	str += activePath.name+".close();\r\n"
	str += "//UIColor.black.setStroke();\r\n"
	str += "//"+activePath.name+".lineWidth = 2.0;\r\n"
	str += "//"+activePath.name+".stroke();\r\n"
	str += "//UIColor.red.setFill();\r\n"
	str += "//"+activePath.name+".fill();\r\n"
 
	txtLayer = activeDocument.artLayers.add();
	txtLayer.kind = LayerKind.TEXT;
	txtLayer.textItem.kind = TextType.PARAGRAPHTEXT;
	txtLayer.textItem.contents = str; //文字列
	txtLayer.textItem.position = Array(10, 30); //位置 Array(x, y)
	txtLayer.textItem.width = "100 px";
	txtLayer.textItem.height = "100 px";
	txtLayer.textItem.size = 3; //フォントサイズ
 
	//alert(str);
}

前回と同じくPlaygroundでみてみます。
inspectorからPlatformをiOSに変更して一旦開き直さないと「UIKitなんてモジュールはないよ!」って怒られるみたいです。

コメントアウトされてる最後の数行をいじれば完成。

import UIKit
import XCPlayground

class Shape : UIView {
    override func draw(_ rect: CGRect) {
        //Photoshopからコピペ
    }
}

let shape : Shape = Shape(frame: CGRect(x:0, y:0, width:427, height:438));
shape.backgroundColor = UIColor.white;
shape.draw(shape.frame)
XCPlaygroundPage.currentPage.liveView = shape

無事出力できました。


カテゴリ: その他
| コメントを残す

着せかえ販売開始しました

何度か審査に落ちた末、やっと審査通過しました。

「同じ着せかえの複製だよ」と言われたり、「実は審査中に製作ガイドがアップデートされてたよ」とか言われたり…
(中には、いやこれ絶対複製じゃない、というのも含まれてましたけど)

初めてだったのでよくわからない部分もありましたがなんとかなりました(笑

よろしくおねがいしますー。

|store_thumbnail|store_thumbnail|
| とまチョップ | とまチョップ ゆるふわクレヨン風|


カテゴリ: その他
| コメントを残す

LINE着せ替え

みなさま、おひさしぶりです。
生きております。とまカフェです。

LINE着せ替えの審査が開始されましたので、市役所へそれ関連の資料を送ってまいりました。
来週には返事が返ってくるでしょうか?

問題はLINEの審査を通過するかですね…
まだ前例が全くありませんから、どんな理由でリジェクトされるかわかりませんからね…

(実は、リンクは貼ってありませんが、審査用に着せ替え用の固定ページがすでに作ってあったりします。そこでどんな着せ替えか見れちゃったりします)


カテゴリ: その他
| コメントを残す

PhotoshopでUIBezierPath

SwiftがさわってみたくてとまチョップスタンプをSwiftで作り直しているのですが、ちょっと吹き出しの形の絵を使いたくなりまして…
でもiPhoneの大きさがたくさん増えたので直接UIImageViewとかに画像を入れるわけにもいかないんですよ。
そこで思い出したのが、いつか見たイラレのベジェ曲線を自動でUIbezierPathに変換してくてるスクリプトです。
その頃は、あー誰かPhotoshopで同じの作ってくれないかなー程度に思っていたのですが、この間.jsxを触ったばかりなのでこれ自分で作れないか、と。
(吹き出しならpopOver使えよというツッコミはなしです)

で、例のごとくリファレンスのようなものを見ながらいじっていたわけですが、まあ分からないんですよ。
全部英語なんですよ。パスの取得すら危うい。

====
とりあえず、

activeDocument.pathItems[].subPathItems[].pathPoints[].anchor[]
activeDocument.pathItems[].subPathItems[].pathPoints[].rightDirection[]
activeDocument.pathItems[].subPathItems[].pathPoints[].leftDirection[]

と入っていけばアンカーポイントは取れるっぽかったのでいじってみました。
これでごちゃごちゃ点を一つずつとっていってSwiftの構文に当てはめるだけ!
…と思ったのですがそうは問屋が卸しませんよ。

Photoshopって一つのアンカーポイントにつきコントロールポイントが二個ついてるんですよ。
でもSwiftは、一つのにつきコントロールポイントを二つ持っているんですよ。
つまり下の図のように!
▼Swift
Swiftpath

▼Photoshop
photoshoppath

要するにですよ、
Photoshopで「二つの点をつなげて曲線を作ったー」
と思っていたら実は
Swiftでは「一つ辺を書いて曲げたー」
という意味だったってことなんですよ

プログラム的には、何て厄介なことしてくれたんだ、ということですよ
for文の一番最初と最後で分岐して…という作業が増えるわけですから。

…ではSwiftでUIBezierPathを描く方法に当てはめていきましょう。

let path : UIBezierPath = UIBezierPath();
path.moveToPoint(CGPoint);
path.addCurveToPoint(CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint);
path.closePath();

こんな感じですかね

エラー処理を加え、お節介なstroke()とfill()を入れ、横書きパラグラフ文字ツールで表示するようにして完成です。
こんな感じになりました
変換したいパスを選択してスクリプトを実行するとUIBezierPathのコードに変換します。

// Only run if we're in a supported version of Photoshop
// Note, removing this will not make the script work, as it depends on
// core application code, it just makes it fail better.
if (!app.featureEnabled("ImageStack Creation")) {
	throw (new Error(localize("$$$/JavaScripts/Statistics/CommandNotAvailable=The Statistics feature is not available in this version of Photoshop.")));
}

try{
	makePath();
}catch(err){
	alert(err);
}

function makePath(){
//	preferences.rulerUnits = Units.PIXELS;
	height = activeDocument.height.value
	width = activeDocument.width.value
	var str="//let height : CGFloat = "+String(height)+";\n";
	str += "//let width : CGFloat = "+String(width)+";\n";
	if(activeDocument.pathItems.length == 0){
		throw "パスを選択してください。";
	}
	for(var i=0; i<activeDocument.pathItems.length; i+=1){
		var activePath = activeDocument.pathItems[i];
		str += "let "+activePath.name+" : UIBezierPath = UIBezierPath();\n";
		for(var j=0; j<activePath.subPathItems.length; j+=1){
			for(var k=0; k<activePath.subPathItems[j].pathPoints.length; k+=1){
				var activePathPoints = activePath.subPathItems[j].pathPoints;
				if(k==0){
					str += activePath.name+".moveToPoint(CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(	activePathPoints[k].anchor[1])+"));\n";
//					str += activePath.name+".moveToPoint(CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(height-	activePathPoints[k].anchor[1])+"));\n";
				}else{
					str += activePath.name+".addCurveToPoint(CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(activePathPoints[k].anchor[1])+"),\n";
					str += "controlPoint1: CGPoint(x: "+String(activePathPoints[k-1].leftDirection[0])+", y: "+String(activePathPoints[k-1].leftDirection[1])+"),\n";
					str += "controlPoint2: CGPoint(x: "+String(activePathPoints[k].rightDirection[0])+", y: "+String(activePathPoints[k].rightDirection[1])+"));\n";
//					str += activePath.name+".addCurveToPoint(CGPoint(x: "+String(activePathPoints[k].anchor[0])+", y: "+String(height-activePathPoints[k].anchor[1])+"),\n";
//					str += "controlPoint1: CGPoint(x: "+String(activePathPoints[k-1].leftDirection[0])+", y: "+String(height-activePathPoints[k-1].leftDirection[1])+"),\n";
//					str += "controlPoint2: CGPoint(x: "+String(activePathPoints[k].rightDirection[0])+", y: "+String(height-activePathPoints[k].rightDirection[1])+"));\n";
				}
			}
				str += activePath.name+".addCurveToPoint(CGPoint(x: "+String(activePathPoints[0].anchor[0])+", y: "+String(activePathPoints[0].anchor[1])+"),\n";
				str += "controlPoint1: CGPoint(x: "+String(activePathPoints[activePath.subPathItems[j].pathPoints.length-1].leftDirection[0])+", y: "+String(activePathPoints[activePath.subPathItems[j].pathPoints.length-1].leftDirection[1])+"),\n";
				str += "controlPoint2: CGPoint(x: "+String(activePathPoints[0].rightDirection[0])+", y: "+String(activePathPoints[0].rightDirection[1])+"));\n";
		}
	}
	
	str += activePath.name+".closePath();\n"
	str += "//UIColor.blackColor().setStroke();\n"
	str += "//"+activePath.name+".lineWidth = 2.0;\n"
	str += "//"+activePath.name+".stroke();\n"
	str += "//UIColor.redColor().setFill();\n"
	str += "//"+activePath.name+".fill();\n"

	txtLayer = activeDocument.artLayers.add();
	txtLayer.kind = LayerKind.TEXT;
	txtLayer.textItem.kind = TextType.PARAGRAPHTEXT;
	txtLayer.textItem.contents = str; //文字列
	txtLayer.textItem.position = Array(10, 30); //位置 Array(x, y)
	txtLayer.textItem.width = "100 px";
	txtLayer.textItem.height = "100 px";
	txtLayer.textItem.size = 3; //フォントサイズ

	//alert(str);
}

こんなかんじになりました
過程1

試しにPlayGroundで見てみます

import UIKit
import XCPlayground
class Test : UIView {
    override func drawRect(rect: CGRect) {
//Photoshopからコピペ
    }
}
let test : Test = Test(frame: CGRectMake(0,0,347, 347));
test.backgroundColor = UIColor.whiteColor();
test.drawRect(test.frame)
XCPlaygroundPage.currentPage.liveView = test

うまくいっているようです(が、let test : Test = Test(frame: CGRectMake(0,0,347, 347));がまだ気になります。→気にしないことにしました。)
過程2

あとはCGAffineTransformScaleで大きさを調整すれば完成ですかね〜


カテゴリ: Photoshop, アプリ制作
| コメントを残す

宛名.jsx

宛名.jsxはPhotoshop用プラグインです。
これは、はがきの表(通信面、住所とか書いてある方)の面の印刷を簡単に行うためのものです。
つまり宛名を印刷するものです。
Officeをうまく使えないので少しでもPhotoshopを多く使おうとして作ったものです
Photohshop最高です

====

宛名.jsxの使用法

  1. まず宛名.jsxを入手して、「/Applications/Adobe Photoshop CC 2015/Presets/Scripts」に移動
  2. NumbersやExcelなどで下のような「郵便番号, 住所, 名前」の順に並んだCSVファイルを作成
    スクリーンショット 2015-12-29 12.11.28スクリーンショット 2015-12-29 11.37.25
  3. Photoshopを起動し、メニューバーの「ファイル>スクリプト>宛名」を選択
  4. 「参照…」ボタンから作成したCSV、pngの保存先を指定(成功すればボタンの横にファイルまでのパスが表示されますが、途中で切れるかもしれません。)
  5. 基本設定
    • pngを出力…最終的な画像を自動で「宛名」フォルダを作成して保存します。同時にpsdも保存します。
    • 最初の一枚のみ作成…プリンタの位置合わせ用の機能です。ずれ具合は「ずれ」の項目で調節してください。
    • アラビア数字を漢数字に変換…”1,2,3″を”一,二,三”に変換します。変換前が全角半角どちらでも問答無用に変換します。
    • ずれ…印刷した時に生じるずれを設定します。設定した値だけ全てのテキストレイヤーを移動します。

    スクリーンショット 2015-12-29 11.44.48

  6. 詳細設定
    • フォント…項目のフォントを変更できます。左でフォントファミリー名、右でスタイルを選択できます。
    • フォントサイズ…フォントを変えた時など、大きさを変えたい時に使用します。
    • 文字間隔…いわゆるカーニングの設定です。

    スクリーンショット 2015-12-29 11.44.51

  7. OKを押して作成します。
  8. 成功すればこんな感じのレイヤー構成になります
    スクリーンショット 2015-12-29 12.59.38
  9. pngを作成にチェックをつけていればこんな具合に出力されます。
    スクリーンショット 2015-12-29 13.03.00
  10. あとはプレビューなどで印刷して終了。

カテゴリ: Photoshop
| コメントを残す

Photoshopで宛名⑦

今回で最後になる予定です

⑩pngで出力
やりたいことは明快です。
「Save as…」で指定パスにpngで保存→ついでにpsdも保存。
activeDocument.layerSets.add();で追加したレイヤーグループを

for(i=0; i

とでもしてグループごとに(人ごとに)処理していきます

グループごとにpngに書き出したいので
一番下のグループから一つづつ表示して順次出力していきます。

for(i=0; i

一旦全てのグループを非表示にしてからi番目のグループを表示します。
saveAs(saveIn[, options][, asCopy][, extensionType]) でセーブしていきます。

pngFilePath = new File(folderPath+"/"+app.activeDocument.layerSets[i].name+".png");
pngOpt = new PNGSaveOptions();
pngOpt.interlaced = false;
	
activeDocument.saveAs(pngFilePath, pngOpt, true, Extension.LOWERCASE);
パス"指定したフォルダ名/現在ドキュメント名/表示されているグループ名.png"、
インターレースなし、
コピー、
小文字(?)(他がUPPERCASEとNONEだったので…)
で保存。

====

あとはついでのpsd保存。

パス"指定したフォルダ名/現在ドキュメント名.psd"、
レイヤー残す、
コピーじゃない、
小文字(?)
で保存。
psdFilePath = new File(folderPath+".psd");
psdOpt = new PhotoshopSaveOptions();
psdOpt.layers = true;
activeDocument.saveAs(psdFilePath, psdOpt, false, Extension.LOWERCASE);

...できました
欲しかった全ての機能を実現できました(印刷以外は)
年賀状を送る時期を過ぎてから始めましたが、なんとか年内に完成しました。
とりあえず置いておきますが、JavaScript初めてなので色々許してください
宛名.jsx
おきまりの文句ですがこのファイルを使って生じたいかなる損失に対しても私は一切の責任を負いませんよ

とりあえず使い方を書かねば…
来年まで自分も覚えている自信がありません(^^;;


カテゴリ: Photoshop
| コメントを残す

Photoshopで宛名書き⑥

前回に引き続きphotoshopで宛名です
⑨プリンタの位置ずれの調整はダイアログに追加する形で実現していきますよ。
1画面に収めるのは辛くなってきたのでタブ(「基本」と「詳細」)を追加します。
menu.tabParent = menu.add(“tabbedpanel”,[10,10,width-110,height-10]);
でwindowにTabbedPanelを追加します。
これはTabを追加するための親レイヤーのようなもののようです。

ではTabを追加していきます。
menu.tabParent.tab1 = menu.tabParent.add(“tab”,undefined,”基本”);
大きさは親TabbedPanelで指定したものと同じになるからundefinedにしろ、と書いてありましたのでおとなしくundefinedにしておきました
addしていけばどんどんタブを増やせるようです。
====

ちなみに実際に表示されるときには一番最後に追加したタブが表示されるようなので
menu.tabParent.selection = 0;
として一番最初に追加したタブが表示されるようにしておきました。

あとはmenu.tabParent.tab1.add()でどんどん中身を追加していきます
そこでついでに追加しようとした保存先選択ボタンで大きく引っかかったのでメモ。
フォルダを選択するダイアログを表示したかったのですが、File.openDialog()ではフォルダは選択できませんでした。
Folder.selectDialog();というのがありました。
このままではフォルダまでのパスがわからないので

menu.btnFolderBrowse.onClick = function () {
	var selectedFolder = Folder.selectDialog();
	var folderPath = selectedFolder.fsName;
}

btnBrowseをクリックした時にselectedFolder.fsNameでパスを取得するようにしました

あとはごりごりUIを作っていきます。

できました。
こんな感じです
スクリーンショット 2015-12-28 22.55.47

ずれで入力する値は直接使うとpixelになってしまうので一旦(1/25.4)をかけてinchに変換。で、今回は300ppiですので300をかけて
parseFloat(textField.text)*(1/25.4)*300
と言う感じにmmをpixelに変換しました。


カテゴリ: Photoshop
| コメントを残す

Photoshopで宛名⑤

⑧フォントの指定は大変でした…
これまではAdobeのJAVASCRIPT TOOLS GUIDEを見て書いていたのですが、今度は何の情報もない。
とりあえず

dialog = new Window('dialog', '宛名', [x, y, y + width, x + height]);
dialog.show();

でよくPhotoshopの「ファイル>スクリプト」でよく見るようなダイアログが表示されたのでこれにボタンやドロップダウンメニューを追加していきます。
そこで検索したらたまたま見つけました。
ADOBE PHOTOSHOP CS5 JAVASCRIPT SCRIPTING REFERENCE
こんないいものがあるではないか。でも欲しい情報がちょっと足りなかったりするのでTOOLS GUIDEの方も併用していきます。
====

menu = new Window('dialog', '宛名', undefined);
menu.pnl1 = menu.add("panel", undefined,"郵便番号");
menu.sizeText1 = menu.pnl1.add("statictext",[ 10,50,110,70], "フォントサイズ:");
menu.pnl1.eText = menu.pnl1.add("edittext",  [110,50,145,70], 18);
menu.ptText1 = menu.pnl1.add("statictext",[150,50,200,70], "pt");
menu.fontText1 = menu.pnl1.add("statictext",[ 10,20,110,40], "   フォント:");
menu.pnl1.drop1 = menu.pnl1.add("dropdownlist", [110,20,260,40], );	
menu.center();		
menu.show();

とりあえずこんな感じで場所を調整しつつドロップダウンリストを一つ作成してダイアログを表示。
キャンセルボタンがないのでescキーでダイアログを消しました
そして、menu.center();で真ん中にダイアログを表示できるらしい表示できるらしいので追加。

スクリーンショット 2015-12-28 15.27.08

あとは見た目がひどいことになってるので
menu.pnl1 = menu.add(“panel”, [0, 0, 300, 100],”郵便番号”);
としてねらった通りの表示をするように調整しました

ドロップダウンリストの中身を決めたいので入れていきます
ドロップダウンを二つ作って、左を「ヒラギノ角ゴ ProN」、右を「W3」というふうな表示にしたいのでapp.fontsから取得。

var fontFamilyArr = new Array();
var fontArr = new Array();
var tmpFontArr = new Array();
var j=0, k=1, tmp;

tmpFontArr[0] = app.fonts[0];
tmp = app.fonts[0].family;
for (i=1; i<app.fonts.length; i++){  //フォントの数だけ回す
	if(tmp == app.fonts[i].family){  //一個前(==tmp)と同じフォントファミリーなら
		tmpFontArr[k] = app.fonts[i];  //tmpFontArrに該当フォントを入れる
		k++;  //次へ
	}else{  //一個前(==tmp)と違うフォントファミリーなら
		fontArr[j] = tmpFontArr; //tmpFontArrにたまった内容をfontArrに追加(二次元配列)
		tmpFontArr = []; //初期化
		tmpFontArr[0] = app.fonts[i]; //フォントファミリー内の一番最初のフォントを追加
		fontFamilyArr[j] = app.fonts[i-1].family; //フォントファミリー名を追加
		k = 1;  //二行上でk=0の分を追加済みなので
		j ++;  //次へ
	}
	tmp = app.fonts[i].family;  //今のフォントファミリー名をとっておく
}

最終的にfontFamilyArrにはフォントファミリー名が被らないように、fontArrには{{font, font},{font, font}}という感じでフォントファミリーごとに入っていることになります。

で、さっきのものとつなげてこんな感じになりました。

var fontFamilyArr = new Array();
var fontArr = new Array();
var tmpFontArr = new Array();
var j=0, k=1, tmp;

tmpFontArr[0] = app.fonts[0];
	tmp = app.fonts[0].family;
	for (i=1; i<app.fonts.length; i++){
		if(tmp == app.fonts[i].family){
			tmpFontArr[k] = app.fonts[i];
			k++;
		}else{
			fontArr[j] = tmpFontArr;
			tmpFontArr = [];
			tmpFontArr[0] = app.fonts[i];
			fontFamilyArr[j] = app.fonts[i-1].family;
			k = 1;
			j ++;
		}
		tmp = app.fonts[i].family;
	}

menu = new Window('dialog', '宛名', undefined);
menu.pnl1 = menu.add("panel", [0, 0, 400, 100],"郵便番号");
menu.sizeText1 = menu.pnl1.add("statictext",[ 10,50,110,70], "フォントサイズ:");
menu.pnl1.eText = menu.pnl1.add("edittext",  [110,50,145,70], 18);
menu.ptText1 = menu.pnl1.add("statictext",[150,50,200,70], "pt");
menu.fontText1 = menu.pnl1.add("statictext",[ 10,20,110,40], "   フォント:");
menu.pnl1.drop1 = menu.pnl1.add("dropdownlist", [110,20,310,40], fontFamilyArr);
menu.pnl1.drop2 = menu.pnl1.add("dropdownlist", [300,20,360,40], );
menu.center();
menu.show();

あとは.onChangeでdrop1の変更を受け取ってdrop2の中身を変えてます。

menu.pnl1.drop1.onChange = function () {
	if (this.selection != null) {
		var index = this.selection.index;
		menu.pnl1.drop2.removeAll();
		for(i=0; i<fontArr[index].length; i++){
			menu.pnl1.drop2.add('item', fontArr[index][i].style);
		}
		menu.pnl1.drop2.selection = menu.pnl1.drop2.items[0];
	}
}

OKボタンとキャンセルボタンを追加し、onClickで押された時の挙動を設定して、

	menu.btnOK = menu.add("button", [width - 110, 15, width - 5, 45], "OK");
	menu.btnCancel = menu.add("button", [width - 110, 55, width - 5, 85], "キャンセル");
	menu.btnInit = menu.add("button", [width - 110, 95, width - 5, 125], "初期化");

ごちゃごちゃごりごり3つだからfor文とか使わなくてもいいかな、と思いながら整えますと
こんな感じになりました

スクリーンショット 2015-12-28 16.07.42

今回は終了。座標の調整がちょっと面倒に感じましたが、undefinedでは思ったようにいかないことが多かったので要調査?


カテゴリ: Photoshop
| コメントを残す

Photoshopで宛名書き④

前回の続きです

⑤配列からの文字レイヤー作成ですが、文字レイヤーはほかのレイヤーとは少し機能が異なるようでTextItemというプロパティを持っているらしいです。
とりあえず新規テキストレイヤー作成。

	var newLayer = layerSet.artLayers.add();
	newLayer.kind = LayerKind.TEXT;

それから指定したい値をいじります

	newLayer.textItem.contents = text; //文字列
	newLayer.textItem.position = position; //位置 Array(x, y)
	newLayer.textItem.font = font.postScriptName; //フォント(postScriptNameで指定しないといけないらしい)
	newLayer.textItem.size = size; //フォントサイズ
	newLayer.textItem.direction = dir; //縦書き、横書き(HORIZONTAL, VERTICAL)
	newLayer.textItem.justification = Justification.LEFTJUSTIFIED; //文字揃え
	newLayer.textItem.maximumLetterScaling = newLayer.textItem.minimumLetterScaling = newLayer.textItem.desiredLetterScaling = kering; //文字間隔
	newLayer.textItem.maximumWordScaling = newLayer.textItem.minimumWordScaling = newLayer.textItem.desiredWordScaling = kering*2; //空白との間隔

====

⑥はがきに合わせて文字レイヤーを置く、についてはヒラギノ角ゴ ProNに合わせて以前私が作っていたものの座標を流用しました。
上のpositionに座標が入っています。

⑦は前回クリア済みですので省略。

今回はとりあえずここまで。
今までもまれにソースコードを載せていたことがありましたが、今回ようやくCrayon Syntax Highlighterを入れてみました
次回に続きます


カテゴリ: Photoshop
| コメントを残す